MapperBuilder.java

package tools.jackson.databind.cfg;

import java.security.AccessController;
import java.security.PrivilegedAction;
import java.text.DateFormat;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.UnaryOperator;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonSetter;
import com.fasterxml.jackson.annotation.JsonTypeInfo;

import tools.jackson.core.*;
import tools.jackson.core.util.DefaultPrettyPrinter;
import tools.jackson.core.util.Snapshottable;
import tools.jackson.databind.*;
import tools.jackson.databind.deser.*;
import tools.jackson.databind.ext.javatime.JavaTimeInitializer;
import tools.jackson.databind.introspect.*;
import tools.jackson.databind.jsontype.*;
import tools.jackson.databind.jsontype.impl.DefaultTypeResolverBuilder;
import tools.jackson.databind.jsontype.impl.StdSubtypeResolver;
import tools.jackson.databind.node.JsonNodeFactory;
import tools.jackson.databind.ser.*;
import tools.jackson.databind.type.LogicalType;
import tools.jackson.databind.type.TypeFactory;
import tools.jackson.databind.type.TypeModifier;
import tools.jackson.databind.util.ArrayBuilders;
import tools.jackson.databind.util.LinkedNode;
import tools.jackson.databind.util.RootNameLookup;
import tools.jackson.databind.util.StdDateFormat;

/**
 * Since {@link ObjectMapper} instances are immutable in  Jackson 3.x for full thread-safety,
 * we need means to construct configured instances. This is the shared base API for
 * builders for all types of mappers.
 *
 * @since 3.0
 */
public abstract class MapperBuilder<M extends ObjectMapper,
    B extends MapperBuilder<M,B>>
{
    protected final static long DEFAULT_MAPPER_FEATURES = MapperFeature.collectLongDefaults();
    protected final static int DEFAULT_SER_FEATURES = ConfigFeature.collectFeatureDefaults(SerializationFeature.class);
    protected final static int DEFAULT_DESER_FEATURES = ConfigFeature.collectFeatureDefaults(DeserializationFeature.class);

    protected final static PrettyPrinter DEFAULT_PRETTY_PRINTER = new DefaultPrettyPrinter();
    protected final static AnnotationIntrospector DEFAULT_ANNOTATION_INTROSPECTOR = new JacksonAnnotationIntrospector();

    protected final static PolymorphicTypeValidator DEFAULT_TYPE_VALIDATOR = new DefaultBaseTypeLimitingValidator();

    protected final static AccessorNamingStrategy.Provider DEFAULT_ACCESSOR_NAMING = new DefaultAccessorNamingStrategy.Provider();

    protected final static BaseSettings DEFAULT_BASE_SETTINGS = new BaseSettings(
            DEFAULT_ANNOTATION_INTROSPECTOR,
            null, null, DEFAULT_ACCESSOR_NAMING,
            null, // no default typing, by default
            DEFAULT_TYPE_VALIDATOR, // and polymorphic type by class won't pass either
            StdDateFormat.instance, null,
            Locale.getDefault(),
            null, // to indicate "use Jackson default TimeZone" (UTC)
            Base64Variants.getDefaultVariant(),
            DefaultCacheProvider.defaultInstance(),
            JsonNodeFactory.instance,
            null // ConstructorDetector
    );

    protected final static TypeResolverProvider DEFAULT_TYPE_RESOLVER_PROVIDER = new TypeResolverProvider();

    protected final static AbstractTypeResolver[] NO_ABSTRACT_TYPE_RESOLVERS = new AbstractTypeResolver[0];

    /*
    /**********************************************************************
    /* Basic settings
    /**********************************************************************
     */

    protected BaseSettings _baseSettings;

    /**
     * Underlying stream factory
     */
    protected TokenStreamFactory _streamFactory;

    /**
     * Various configuration setting overrides, both global base settings
     * and per-class overrides.
     */
    protected final ConfigOverrides _configOverrides;

    /**
     * Coercion settings (global, per-type overrides)
     */
    protected final CoercionConfigs _coercionConfigs;

    /*
    /**********************************************************************
    /* Modules
    /**********************************************************************
     */

    /**
     * Modules registered for addition, indexed by registration id.
     */
    protected Map<Object, JacksonModule> _modules;

    /*
    /**********************************************************************
    /* Handlers, introspection
    /**********************************************************************
     */

    /**
     * Specific factory used for creating {@link JavaType} instances;
     * needed to allow modules to add more custom type handling
     * (mostly to support types of non-Java JVM languages)
     */
    protected TypeFactory _typeFactory;

    /**
     * Introspector used to figure out Bean properties needed for bean serialization
     * and deserialization. Overridable so that it is possible to change low-level
     * details of introspection, like adding new annotation types.
     */
    protected ClassIntrospector _classIntrospector;

    /**
     * Entity responsible for construction actual type resolvers
     * ({@link tools.jackson.databind.jsontype.TypeSerializer}s,
     * {@link tools.jackson.databind.jsontype.TypeDeserializer}s).
     */
    protected TypeResolverProvider _typeResolverProvider;

    protected SubtypeResolver _subtypeResolver;

    /**
     * Handler responsible for resolving mix-in classes registered, if any.
     */
    protected MixInHandler _mixInHandler;

    /*
    /**********************************************************************
    /* Factories for serialization
    /**********************************************************************
     */

    /**
     * {@link SerializationContexts} to use as factory for stateful {@link SerializationContext}s
     */
    protected SerializationContexts _serializationContexts;

    protected SerializerFactory _serializerFactory;

    protected FilterProvider _filterProvider;

    protected PrettyPrinter _defaultPrettyPrinter;

    /*
    /**********************************************************************
    /* Factories etc for deserialization
    /**********************************************************************
     */

    /**
     * Factory to use for creating per-operation contexts.
     */
    protected DeserializationContexts _deserializationContexts;

    protected DeserializerFactory _deserializerFactory;

    /**
     * Provider for values to inject in deserialized POJOs.
     */
    protected InjectableValues _injectableValues;

    /**
     * Optional handlers that application may register to try to work-around
     * various problem situations during deserialization
     */
    protected LinkedNode<DeserializationProblemHandler> _problemHandlers;

    protected AbstractTypeResolver[] _abstractTypeResolvers;

    /*
    /**********************************************************************
    /* Handlers/factories, other:
    /**********************************************************************
     */

    /**
     * Explicitly configured default {@link ContextAttributes}, if any.
     */
    protected ContextAttributes _defaultAttributes;

    /*
    /**********************************************************************
    /* Feature flags: ser, deser
    /**********************************************************************
     */

    /**
     * Set of shared mapper features enabled.
     */
    protected long _mapperFeatures;

    /**
     * Set of {@link SerializationFeature}s enabled.
     */
    protected int _serFeatures;

    /**
     * Set of {@link DeserializationFeature}s enabled.
     */
    protected int _deserFeatures;

    protected DatatypeFeatures _datatypeFeatures;

    /*
    /**********************************************************************
    /* Feature flags: generation, parsing
    /**********************************************************************
     */

    /**
     * States of {@link StreamReadFeature}s to enable/disable.
     */
    protected int _streamReadFeatures;

    /**
     * States of {@link StreamWriteFeature}s to enable/disable.
     */
    protected int _streamWriteFeatures;

    /**
     * Optional per-format parser feature flags.
     */
    protected int _formatReadFeatures;

    /**
     * Optional per-format generator feature flags.
     */
    protected int _formatWriteFeatures;

    /*
    /**********************************************************************
    /* Transient state
    /**********************************************************************
     */

    /**
     * Configuration state after direct access, immediately before registration
     * of modules (if any) and construction of actual mapper. Retained after
     * first access, and returned from {@link #saveStateApplyModules()}, to
     * allow future "rebuild".
     */
    protected transient MapperBuilderState _savedState;

    /*
    /**********************************************************************
    /* Life-cycle
    /**********************************************************************
     */

    protected MapperBuilder(TokenStreamFactory streamFactory)
    {
        _streamFactory = streamFactory;
        _baseSettings = DEFAULT_BASE_SETTINGS;
        _configOverrides = new ConfigOverrides();
        _coercionConfigs = new CoercionConfigs();
        _modules = null;

        _streamReadFeatures = streamFactory.getStreamReadFeatures();
        _streamWriteFeatures = streamFactory.getStreamWriteFeatures();
        _formatReadFeatures = streamFactory.getFormatReadFeatures();
        _formatWriteFeatures = streamFactory.getFormatWriteFeatures();

        _mapperFeatures = DEFAULT_MAPPER_FEATURES;
        // Some overrides we may need based on format
        if (streamFactory.requiresPropertyOrdering()) {
            _mapperFeatures |= MapperFeature.SORT_PROPERTIES_ALPHABETICALLY.getLongMask();
        }
        _deserFeatures = DEFAULT_DESER_FEATURES;
        _serFeatures = DEFAULT_SER_FEATURES;
        _datatypeFeatures = DatatypeFeatures.defaultFeatures();

        _typeFactory = null;
        _classIntrospector = null;
        _typeResolverProvider = null;
        _subtypeResolver = null;
        _mixInHandler = null;

        _serializerFactory = null;
        _serializationContexts = null;
        _filterProvider = null;

        _deserializerFactory = null;
        _deserializationContexts = null;
        _injectableValues = null;
        _problemHandlers = null;
        _abstractTypeResolvers = NO_ABSTRACT_TYPE_RESOLVERS;

        _defaultAttributes = null;
    }

    /**
     * Constructor used to support "rebuild", starting with a previously taken
     * snapshot, in order to create mappers that start with a known state of
     * configuration, including a set of modules to register.
     */
    protected MapperBuilder(MapperBuilderState state)
    {
        _streamFactory = state._streamFactory;
        _baseSettings = state._baseSettings;
        _configOverrides = Snapshottable.takeSnapshot(state._configOverrides);
        _coercionConfigs = Snapshottable.takeSnapshot(state._coercionConfigs);

        _streamReadFeatures = state._streamReadFeatures;
        _streamWriteFeatures = state._streamWriteFeatures;
        _formatReadFeatures = state._formatReadFeatures;
        _formatWriteFeatures = state._formatWriteFeatures;
        _mapperFeatures = state._mapperFeatures;
        _deserFeatures = state._deserFeatures;
        _serFeatures = state._serFeatures;
        _datatypeFeatures = state._datatypeFeatures;

        // Handlers, introspection
        _typeFactory = Snapshottable.takeSnapshot(state._typeFactory);

        _classIntrospector = state._classIntrospector;
        _typeResolverProvider = state._typeResolverProvider;
        _subtypeResolver = Snapshottable.takeSnapshot(state._subtypeResolver);
        _mixInHandler = (MixInHandler) Snapshottable.takeSnapshot(state._mixInHandler);

        // Factories for serialization
        _serializationContexts = state._serializationContexts;
        _serializerFactory = state._serializerFactory;
        _filterProvider = state._filterProvider;
        _defaultPrettyPrinter = state._defaultPrettyPrinter;

        // Factories for deserialization
        _deserializationContexts = state._deserializationContexts;
        _deserializerFactory = state._deserializerFactory;
        _injectableValues = Snapshottable.takeSnapshot(state._injectableValues);
        _problemHandlers = state._problemHandlers;
        _abstractTypeResolvers = state._abstractTypeResolvers;

        // Factories/handlers, other
        _defaultAttributes = Snapshottable.takeSnapshot(state._defaultAttributes);

        // Modules
        if (state._modules == null) {
            _modules = null;
        } else {
            _modules = new LinkedHashMap<>();
            for (JacksonModule mod : state._modules) {
                addModule(mod);
            }
        }
    }

    /*
    protected MapperBuilder(MapperBuilder<?,?> base)
    {
        _streamFactory = base._streamFactory;
        _baseSettings = base._baseSettings;
        _configOverrides = base._configOverrides;
        _coercionConfigs = base._coercionConfigs;

        _mapperFeatures = base._mapperFeatures;
        _serFeatures = base._serFeatures;
        _deserFeatures = base._deserFeatures;

        _streamReadFeatures = base._streamReadFeatures;
        _stremWriteFeatures = base._stremWriteFeatures;
        _formatReadFeatures = base._formatReadFeatures;
        _formatWriteFeatures = base._formatWriteFeatures;
        _datatypeFeatures = base._datatypeFeatures;

        _typeFactory = base._typeFactory;
        _classIntrospector = base._classIntrospector;
        _typeResolverProvider = base._typeResolverProvider;
        _subtypeResolver = base._subtypeResolver;
        _mixInHandler = base._mixInHandler;

        _serializerFactory = base._serializerFactory;
        _serializationContexts = base._serializationContexts;
        _filterProvider = base._filterProvider;

        _deserializerFactory = base._deserializerFactory;
        _deserializationContext = base._deserializationContext;
        _injectableValues = base._injectableValues;
        _problemHandlers = base._problemHandlers;
        _abstractTypeResolvers = base._abstractTypeResolvers;
        _cacheProvider = base._cacheProvider;
    }
    */

    /*
    /**********************************************************************
    /* Methods for actual build process
    /**********************************************************************
     */

    /**
     * Method to call to create actual mapper instance.
     *<p>
     * Implementation detail: usually construction occurs by passing {@code this}
     * builder instance to constructor of specific mapper type builder builds.
     */
    public abstract M build();

    /**
     * Method called by mapper being constructed to first save state (delegated to
     * {code _saveState()} method), then apply modules (if any), and then return
     * the saved state (but retain reference to it). If method has been called previously,
     * it will simply return retained state.
     */
    public MapperBuilderState saveStateApplyModules()
    {
        if (_savedState == null) {
            _savedState = _saveState();
            ModuleContextBase ctxt = _constructModuleContext();
            JavaTimeInitializer.getInstance().setupModule(ctxt);
            if (_modules != null) {
                _modules.values().forEach(m -> m.setupModule(ctxt));
                // and since context may buffer some changes, ensure those are flushed:
                ctxt.applyChanges(this);
            }
        }
        return _savedState;
    }

    protected ModuleContextBase _constructModuleContext() {
        return new ModuleContextBase(this, _configOverrides);
    }

    protected abstract MapperBuilderState _saveState();

    /*
    /**********************************************************************
    /* Secondary factory methods
    /**********************************************************************
     */

    public SerializationConfig buildSerializationConfig(ConfigOverrides configOverrides,
            MixInHandler mixins, TypeFactory tf, ClassIntrospector classIntr, SubtypeResolver str,
            RootNameLookup rootNames,
            FilterProvider filterProvider)
    {
        return new SerializationConfig(this,
                _mapperFeatures, _serFeatures, _streamWriteFeatures, _formatWriteFeatures,
                configOverrides,
                tf, classIntr, mixins, str,
                defaultAttributes(), rootNames,
                filterProvider);
    }

    public DeserializationConfig buildDeserializationConfig(ConfigOverrides configOverrides,
            MixInHandler mixins, TypeFactory tf, ClassIntrospector classIntr, SubtypeResolver str,
            RootNameLookup rootNames,
            CoercionConfigs coercionConfigs)
    {
        return new DeserializationConfig(this,
                _mapperFeatures, _deserFeatures, _streamReadFeatures, _formatReadFeatures,
                configOverrides, coercionConfigs,
                tf, classIntr, mixins, str,
                defaultAttributes(), rootNames,
                _abstractTypeResolvers);
    }

    /*
    /**********************************************************************
    /* Accessors, features
    /**********************************************************************
     */

    public boolean isEnabled(MapperFeature f) {
        return f.enabledIn(_mapperFeatures);
    }
    public boolean isEnabled(DeserializationFeature f) {
        return f.enabledIn(_deserFeatures);
    }
    public boolean isEnabled(SerializationFeature f) {
        return f.enabledIn(_serFeatures);
    }
    public boolean isEnabled(DatatypeFeature f) {
        return _datatypeFeatures.isEnabled(f);
    }
    public boolean isEnabled(StreamReadFeature f) {
        return f.enabledIn(_streamReadFeatures);
    }
    public boolean isEnabled(StreamWriteFeature f) {
        return f.enabledIn(_streamWriteFeatures);
    }

    public DatatypeFeatures datatypeFeatures() {
        return _datatypeFeatures;
    }

    /*
    /**********************************************************************
    /* Accessors, base settings
    /**********************************************************************
     */

    public BaseSettings baseSettings() {
        return _baseSettings;
    }

    public TokenStreamFactory streamFactory() {
        return _streamFactory;
    }

    public AnnotationIntrospector annotationIntrospector() {
        return _baseSettings.getAnnotationIntrospector();
    }

    /**
     * Overridable method for changing default {@link ContextAttributes} instance to use
     * if not explicitly specified during build process.
     */
    public ContextAttributes defaultAttributes() {
        // 01-Feb-2020, tatu: Looks different from pattern used with other
        //    defaults; seems better not to change state of builder?
        if (_defaultAttributes == null) {
            return ContextAttributes.getEmpty();
        }
        return _defaultAttributes;
    }

    /**
     * Overridable method for changing default {@link ContextAttributes} instance to use
     * if not explicitly specified during build process.
     */
    protected ContextAttributes _defaultDefaultAttributes() {
        return ContextAttributes.getEmpty();
    }

    /*
    /**********************************************************************
    /* Accessors, introspection
    /**********************************************************************
     */

    public TypeFactory typeFactory() {
        if (_typeFactory == null) {
            _typeFactory = _defaultTypeFactory();
        }
        return _typeFactory;
    }

    /**
     * Overridable method for changing default {@link TypeFactory} instance to use
     */
    protected TypeFactory _defaultTypeFactory() {
        return TypeFactory.createDefaultInstance();
    }

    public ClassIntrospector classIntrospector() {
        if (_classIntrospector == null) {
            _classIntrospector = _defaultClassIntrospector();
        }
        return _classIntrospector;
    }

    /**
     * Overridable method for changing default {@link SubtypeResolver} instance to use
     */
    protected ClassIntrospector _defaultClassIntrospector() {
        return new BasicClassIntrospector();
    }

    public TypeResolverProvider typeResolverProvider() {
        if (_typeResolverProvider == null) {
            _typeResolverProvider = _defaultTypeResolverProvider();
        }
        return _typeResolverProvider;
    }

    /**
     * Overridable method for changing default {@link TypeResolverProvider} instance to use
     */
    protected TypeResolverProvider _defaultTypeResolverProvider() {
        return new TypeResolverProvider();
    }

    public SubtypeResolver subtypeResolver() {
        if (_subtypeResolver == null) {
            _subtypeResolver = _defaultSubtypeResolver();
        }
        return _subtypeResolver;
    }

    /**
     * Overridable method for changing default {@link SubtypeResolver} prototype
     * to use.
     */
    protected SubtypeResolver _defaultSubtypeResolver() {
        return new StdSubtypeResolver();
    }

    public MixInHandler mixInHandler() {
        if (_mixInHandler == null) {
            _mixInHandler = _defaultMixInHandler();
        }
        return _mixInHandler;
    }

    /**
     * Overridable method for changing default {@link MixInHandler} prototype
     * to use.
     */
    protected MixInHandler _defaultMixInHandler() {
        return new MixInHandler(null);
    }

    /*
    /**********************************************************************
    /* Accessors, serialization factories, related
    /**********************************************************************
     */

    public SerializationContexts serializationContexts() {
        if (_serializationContexts == null) {
            _serializationContexts = _defaultSerializationContexts();
        }
        return _serializationContexts;
    }

    /**
     * Overridable method for changing default {@link SerializationContext} prototype
     * to use.
     */
    protected SerializationContexts _defaultSerializationContexts() {
        return new SerializationContexts.DefaultImpl();
    }

    public SerializerFactory serializerFactory() {
        if (_serializerFactory == null) {
            _serializerFactory = _defaultSerializerFactory();
        }
        return _serializerFactory;
    }

    protected SerializerFactory _defaultSerializerFactory() {
        return BeanSerializerFactory.instance;
    }

    public FilterProvider filterProvider() {
        return _filterProvider;
    }

    public PrettyPrinter defaultPrettyPrinter() {
        if (_defaultPrettyPrinter == null) {
            _defaultPrettyPrinter = _defaultPrettyPrinter();
        }
        return _defaultPrettyPrinter;
    }

    protected PrettyPrinter _defaultPrettyPrinter() {
        return DEFAULT_PRETTY_PRINTER;
    }

    /*
    /**********************************************************************
    /* Accessors, deserialization factories, related
    /**********************************************************************
     */

    public DeserializationContexts deserializationContexts() {
        if (_deserializationContexts == null) {
            _deserializationContexts = _defaultDeserializationContexts();
        }
        return _deserializationContexts;
    }

    /**
     * Overridable method for changing default {@link SerializationContext} prototype
     * to use.
     */
    protected DeserializationContexts _defaultDeserializationContexts() {
        return new DeserializationContexts.DefaultImpl();
    }

    public DeserializerFactory deserializerFactory() {
        if (_deserializerFactory == null) {
            _deserializerFactory = _defaultDeserializerFactory();
        }
        return _deserializerFactory;
    }

    DeserializerFactory _defaultDeserializerFactory() {
        return BeanDeserializerFactory.instance;
    }

    public InjectableValues injectableValues() {
        return _injectableValues;
    }

    public LinkedNode<DeserializationProblemHandler> deserializationProblemHandlers() {
        return _problemHandlers;
    }

    /*
    /**********************************************************************
    /* Changing features: mapper, ser, deser
    /**********************************************************************
     */

    public B enable(MapperFeature... features) {
        for (MapperFeature f : features) {
            _mapperFeatures |= f.getLongMask();
        }
        return _this();
    }

    public B disable(MapperFeature... features) {
        for (MapperFeature f : features) {
            _mapperFeatures &= ~f.getLongMask();
        }
        return _this();
    }

    public B configure(MapperFeature feature, boolean state)
    {
        if (state) {
            _mapperFeatures |= feature.getLongMask();
        } else {
            _mapperFeatures &= ~feature.getLongMask();
        }
        return _this();
    }

    public B enable(SerializationFeature... features) {
        for (SerializationFeature f : features) {
            _serFeatures |= f.getMask();
        }
        return _this();
    }

    public B disable(SerializationFeature... features) {
        for (SerializationFeature f : features) {
            _serFeatures &= ~f.getMask();
        }
        return _this();
    }

    public B configure(SerializationFeature feature, boolean state)
    {
        if (state) {
            _serFeatures |= feature.getMask();
        } else {
            _serFeatures &= ~feature.getMask();
        }
        return _this();
    }

    public B enable(DeserializationFeature... features) {
        for (DeserializationFeature f : features) {
            _deserFeatures |= f.getMask();
        }
        return _this();
    }

    public B disable(DeserializationFeature... features) {
        for (DeserializationFeature f : features) {
            _deserFeatures &= ~f.getMask();
        }
        return _this();
    }

    public B configure(DeserializationFeature feature, boolean state)
    {
        if (state) {
            _deserFeatures |= feature.getMask();
        } else {
            _deserFeatures &= ~feature.getMask();
        }
        return _this();
    }

    public B enable(DatatypeFeature... features) {
        _datatypeFeatures = _datatypeFeatures.withFeatures(features);
        return _this();
    }

    public B disable(DatatypeFeature... features) {
        _datatypeFeatures = _datatypeFeatures.withoutFeatures(features);
        return _this();
    }

    public B configure(DatatypeFeature feature, boolean state) {
        if (state) {
            _datatypeFeatures = _datatypeFeatures.with(feature);
        } else {
            _datatypeFeatures = _datatypeFeatures.without(feature);
        }
        return _this();
    }

    /**
     * The builder returned uses default settings more closely
     * matching the default configuration used in Jackson 2.x versions.
     * It affects:
     * <ul>
     * <li>{@link DateTimeFeature}s</li>
     * <li>{@link DeserializationFeature}s</li>
     * <li>{@link EnumFeature}s</li>
     * <li>{@link JsonNodeFeature}s</li>
     * <li>{@link MapperFeature}s</li>
     * <li>{@link SerializationFeature}s</li>
     *  </ul>
     * <p>
     * This method is still a work in progress and may not yet fully replicate the
     * default settings of Jackson 2.x.
     * </p>
     */
    public B configureForJackson2() {
        return disable(DateTimeFeature.ONE_BASED_MONTHS)
                .enable(DateTimeFeature.WRITE_DATES_AS_TIMESTAMPS)
                .enable(DateTimeFeature.WRITE_DURATIONS_AS_TIMESTAMPS)
                .disable(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES)
                .disable(DeserializationFeature.FAIL_ON_TRAILING_TOKENS)
                .enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
                .disable(EnumFeature.READ_ENUMS_USING_TO_STRING)
                .disable(EnumFeature.WRITE_ENUMS_USING_TO_STRING)
                .enable(JsonNodeFeature.STRIP_TRAILING_BIGDECIMAL_ZEROES)
                .enable(MapperFeature.ALLOW_FINAL_FIELDS_AS_MUTATORS)
                .disable(MapperFeature.DETECT_PARAMETER_NAMES) // [databind#5314]
                .disable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY)
                .enable(MapperFeature.USE_GETTERS_AS_SETTERS)
                .enable(SerializationFeature.FAIL_ON_EMPTY_BEANS)
                ;
    }

    /*
    /**********************************************************************
    /* Changing features: parser, generator
    /**********************************************************************
     */

    public B enable(StreamReadFeature... features) {
        for (StreamReadFeature f : features) {
            _streamReadFeatures |= f.getMask();
        }
        return _this();
    }

    public B disable(StreamReadFeature... features) {
        for (StreamReadFeature f : features) {
            _streamReadFeatures &= ~f.getMask();
        }
        return _this();
    }

    public B configure(StreamReadFeature feature, boolean state) {
        if (state) {
            _streamReadFeatures |= feature.getMask();
        } else {
            _streamReadFeatures &= ~feature.getMask();
        }
        return _this();
    }

    public B enable(StreamWriteFeature... features) {
        for (StreamWriteFeature f : features) {
            _streamWriteFeatures |= f.getMask();
        }
        return _this();
    }

    public B disable(StreamWriteFeature... features) {
        for (StreamWriteFeature f : features) {
            _streamWriteFeatures &= ~f.getMask();
        }
        return _this();
    }

    public B configure(StreamWriteFeature feature, boolean state) {
        if (state) {
            _streamWriteFeatures |= feature.getMask();
        } else {
            _streamWriteFeatures &= ~feature.getMask();
        }
        return _this();
    }

    /*
    /**********************************************************************
    /* Changing settings, config overrides
    /**********************************************************************
     */

    /**
     * Method for changing config overrides for specific type, through
     * callback to specific handler.
     */
    public B withConfigOverride(Class<?> forType,
            Consumer<MutableConfigOverride> handler) {
        handler.accept(_configOverrides.findOrCreateOverride(forType));
        return _this();
    }

    /**
     * Method for changing various aspects of configuration overrides.
     */
    public B withAllConfigOverrides(Consumer<ConfigOverrides> handler) {
        handler.accept(_configOverrides);
        return _this();
    }

    /**
     * Method for changing currently configured default {@link VisibilityChecker},
     * object used for determining whether given property element
     * (method, field, constructor) can be auto-detected or not.
     * Checker to modify is used for all POJO types for which there is no specific
     * per-type checker.
     *
     * @param handler Function that is given current default visibility checker and that
     *    needs to return either checker as is, or a new instance created using one or more of
     *    {@code withVisibility} (and similar) calls.
     */
    public B changeDefaultVisibility(UnaryOperator<VisibilityChecker> handler) {
        VisibilityChecker oldV = _configOverrides.getDefaultVisibility();
        VisibilityChecker newV = handler.apply(oldV);
        if (newV != oldV) {
            Objects.requireNonNull(newV, "Cannot assign null default VisibilityChecker");
            _configOverrides.setDefaultVisibility(newV);
        }
        return _this();
    }

    /**
     * Method for changing currently default settings for property inclusion, used for determining
     * whether POJO properties with certain value should be excluded or not: most common case being
     * exclusion of `null` values.
     */
    public B changeDefaultPropertyInclusion(UnaryOperator<JsonInclude.Value> handler) {
        JsonInclude.Value oldIncl = _configOverrides.getDefaultInclusion();
        JsonInclude.Value newIncl = handler.apply(oldIncl);
        if (newIncl != oldIncl) {
            Objects.requireNonNull(newIncl, "Cannot assign null default Property Inclusion");
            _configOverrides.setDefaultInclusion(newIncl);
        }
        //public ObjectMapper setDefaultPropertyInclusion() {
        return _this();
    }

    /**
     * Method for changing currently default settings for handling of `null` values during
     * deserialization, regarding whether they are set as-is, ignored completely, or possible
     * transformed into "empty" value of the target type (if any).
     */
    public B changeDefaultNullHandling(UnaryOperator<JsonSetter.Value> handler) {
        JsonSetter.Value oldIncl = _configOverrides.getDefaultNullHandling();
        JsonSetter.Value newIncl = handler.apply(oldIncl);
        if (newIncl != oldIncl) {
            Objects.requireNonNull(newIncl, "Cannot assign null default Null Handling");
            _configOverrides.setDefaultNullHandling(newIncl);
        }
        return _this();
    }

    /**
     * Method for setting default Setter configuration, regarding things like
     * merging, null-handling; used for properties for which there are
     * no per-type or per-property overrides (via annotations or config overrides).
     */
    public B defaultMergeable(Boolean b) {
        _configOverrides.setDefaultMergeable(b);
        return _this();
    }

    /**
     * Method for setting default Setter configuration, regarding things like
     * merging, null-handling; used for properties for which there are
     * no per-type or per-property overrides (via annotations or config overrides).
     */
    public B defaultLeniency(Boolean b) {
        _configOverrides.setDefaultLeniency(b);
        return _this();
    }

    /**
     * Method for configuring default format settings to use for serialization and deserialization.
     *
     * @since 3.1
     */
    public B defaultFormat(JsonFormat.Value format) {
        _configOverrides.setDefaultFormat(format);
        return _this();
    }

    /*
    /**********************************************************************
    /* Changing settings, coercion config
    /**********************************************************************
     */

    /**
     * Method for changing coercion config for specific logical types, through
     * callback to specific handler.
     */
    public B withCoercionConfig(LogicalType forType,
            Consumer<MutableCoercionConfig> handler) {
        handler.accept(_coercionConfigs.findOrCreateCoercion(forType));
        return _this();
    }

    /**
     * Method for changing coercion config for specific physical type, through
     * callback to specific handler.
     */
    public B withCoercionConfig(Class<?> forType,
            Consumer<MutableCoercionConfig> handler) {
        handler.accept(_coercionConfigs.findOrCreateCoercion(forType));
        return _this();
    }

    /**
     * Method for changing target-type-independent coercion configuration defaults.
     */
    public B withCoercionConfigDefaults(Consumer<MutableCoercionConfig> handler) {
        handler.accept(_coercionConfigs.defaultCoercions());
        return _this();
    }

    // 03-Jun-2020, tatu: Needed at least for snapshotting, if not for other usage...
    /**
     * Method for changing various aspects of configuration overrides.
     */
    public B withAllCoercionConfigs(Consumer<CoercionConfigs> handler) {
        handler.accept(_coercionConfigs);
        return _this();
    }

    /*
    /**********************************************************************
    /* Module registration, discovery, access
    /**********************************************************************
     */

    /**
     * Method that will drop all modules added (via {@link #addModule} and similar
     * calls) to this builder.
     */
    public B removeAllModules() {
        _modules = null;
        // [databind#5481]: invalidate cached state when modules are modified
        _savedState = null;
        return _this();
    }

    /**
     * Method will add given module to be registered when mapper is built, possibly
     * replacing an earlier instance of the module (as specified by its
     * {@link JacksonModule#getRegistrationId()}).
     * Actual registration occurs in addition order (considering last add to count,
     * in case of re-registration for same id) when {@link #build()} is called.
     */
    public B addModule(JacksonModule module)
    {
        Objects.requireNonNull(module, "Cannot add null `JacksonModule`");
        _verifyModuleMetadata(module);
        final Object moduleId = module.getRegistrationId();
        if (_modules == null) {
            _modules = new LinkedHashMap<>();
        } else {
            // Important: since order matters, we won't try to simply replace existing one.
            // Could do in different order (put, and only re-order if there was old value),
            // but simple does it for now.
            _modules.remove(moduleId);
        }

        // 10-Sep-2019, tatu: [databind#2432] Module dependencies; need to add first
        //   but unlike main module, do NOT replace module if already added
        for (JacksonModule dep : module.getDependencies()) {
            _verifyModuleMetadata(dep);
            _modules.putIfAbsent(dep.getRegistrationId(), dep);
        }
        _modules.put(moduleId, module);
        // [databind#5481]: invalidate cached state when modules are modified
        _savedState = null;
        return _this();
    }

    private void _verifyModuleMetadata(JacksonModule module)
    {
        if (module.getModuleName() == null) {
            throw new IllegalArgumentException("Module ("+module.getClass().getName()+") without defined name");
        }
        if (module.version() == null) {
            throw new IllegalArgumentException("Module ("+module.getClass().getName()+") without defined version");
        }
    }

    public B addModules(JacksonModule... modules)
    {
        for (JacksonModule module : modules) {
            addModule(module);
        }
        return _this();
    }

    public B addModules(Iterable<? extends JacksonModule> modules)
    {
        for (JacksonModule module : modules) {
            addModule(module);
        }
        return _this();
    }

    /**
     * Method for locating available methods, using JDK {@link ServiceLoader}
     * facility, along with module-provided SPI.
     *<p>
     * Note that method does not do any caching, so calls should be considered
     * potentially expensive.
     */
    public static List<JacksonModule> findModules() {
        return findModules(null);
    }

    /**
     * Method for locating available methods, using JDK {@link ServiceLoader}
     * facility, along with module-provided SPI.
     *<p>
     * Note that method does not do any caching, so calls should be considered
     * potentially expensive.
     */
    public static List<JacksonModule> findModules(ClassLoader classLoader)
    {
        ArrayList<JacksonModule> modules = new ArrayList<>();
        ServiceLoader<JacksonModule> loader = secureGetServiceLoader(JacksonModule.class, classLoader);
        for (JacksonModule module : loader) {
            modules.add(module);
        }
        return modules;
    }

    private static <T> ServiceLoader<T> secureGetServiceLoader(final Class<T> clazz, final ClassLoader classLoader) {
        final SecurityManager sm = System.getSecurityManager();
        if (sm == null) {
            return (classLoader == null) ?
                    ServiceLoader.load(clazz) : ServiceLoader.load(clazz, classLoader);
        }
        return AccessController.doPrivileged(new PrivilegedAction<ServiceLoader<T>>() {
            @Override
            public ServiceLoader<T> run() {
                return (classLoader == null) ?
                        ServiceLoader.load(clazz) : ServiceLoader.load(clazz, classLoader);
            }
        });
    }

    /**
     * Convenience method that is functionally equivalent to:
     *<code>
     *   addModules(builder.findModules());
     *</code>
     *<p>
     * As with {@link #findModules()}, no caching is done for modules, so for
     * performance reasons it may make sense to cache introspected set of modules
     * if needed multiple times.
     */
    public B findAndAddModules() {
        return addModules(findModules());
    }

    /**
     * Convenience method that is functionally equivalent to:
     *<code>
     *   addModules(builder.findModules(classLoader));
     *</code>
     *<p>
     * As with {@link #findModules(ClassLoader)}, no caching is done for modules, so for
     * performance reasons it may make sense to cache introspected set of modules
     * if needed multiple times.
     */
    public B findAndAddModules(ClassLoader cl) {
        return addModules(findModules(cl));
    }

    /**
     * "Accessor" method that will expose set of registered modules, in addition
     * order, using {@code handler} given.
     */
    public B withModules(Consumer<JacksonModule> handler) {
        if (_modules != null) {
            _modules.values().forEach(handler);
        }
        return _this();
    }

    /*
    /**********************************************************************
    /* Changing base settings
    /**********************************************************************
     */

    public B baseSettings(BaseSettings b) {
        _baseSettings = b;
        return _this();
    }

    /**
     * Method for replacing {@link AnnotationIntrospector} used by the
     * mapper instance to be built.
     * Note that doing this will replace the current introspector, which
     * may lead to unavailability of core Jackson annotations.
     * If you want to combine handling of multiple introspectors,
     * have a look at {@link tools.jackson.databind.introspect.AnnotationIntrospectorPair}.
     *
     * @see tools.jackson.databind.introspect.AnnotationIntrospectorPair
     */
    public B annotationIntrospector(AnnotationIntrospector intr) {
        _baseSettings = _baseSettings.withAnnotationIntrospector(intr);
        return _this();
    }

    /**
     * Method for replacing default {@link ContextAttributes} that the mapper
     * uses: usually one initialized with a set of default shared attributes, but
     * potentially also with a custom implementation.
     *<p>
     * NOTE: instance specified will need to be thread-safe for usage, similar to the
     * default ({@link ContextAttributes.Impl}).
     *
     * @param attrs Default instance to use, if not {@code null}, or {@code null} for "use empty default set".
     *
     * @return This Builder instance to allow call chaining
     */
    public B defaultAttributes(ContextAttributes attrs) {
        _defaultAttributes = attrs;
        return _this();
    }

    /*
    /**********************************************************************
    /* Changing introspection helpers
    /**********************************************************************
     */

    public B typeFactory(TypeFactory f) {
        _typeFactory = f;
        return _this();
    }

    public B addTypeModifier(TypeModifier modifier) {
        // important! Need to use getter, to force lazy construction if need be
        _typeFactory = typeFactory()
                .withModifier(modifier);
        return _this();
    }

    protected B typeResolverProvider(TypeResolverProvider p) {
        _typeResolverProvider = p;
        return _this();
    }

    public B classIntrospector(ClassIntrospector ci) {
        _classIntrospector = ci;
        return _this();
    }

    public B subtypeResolver(SubtypeResolver r) {
        _subtypeResolver = r;
        return _this();
    }

    public B polymorphicTypeValidator(PolymorphicTypeValidator ptv) {
        _baseSettings = _baseSettings.with(ptv);
        return _this();
    }

    /**
     * Method for configuring {@link HandlerInstantiator} to use for creating
     * instances of handlers (such as serializers, deserializers, type and type
     * id resolvers), given a class.
     *
     * @param hi Instantiator to use; if null, use the default implementation
     *
     * @return Builder instance itself to allow chaining
     */
    public B handlerInstantiator(HandlerInstantiator hi) {
        _baseSettings = _baseSettings.with(hi);
        return _this();
    }

    /**
     * Method for configuring {@link PropertyNamingStrategy} to use for adapting
     * POJO property names (internal) into content property names (external)
     *
     * @param s Strategy instance to use; if null, use the default implementation
     *
     * @return Builder instance itself to allow chaining
     */
    public B propertyNamingStrategy(PropertyNamingStrategy s) {
        _baseSettings = _baseSettings.with(s);
        return _this();
    }

    /**
     * Method for configuring {@link EnumNamingStrategy} to use for adapting
     * POJO enum names (internal) into content property names (external)
     *
     * @param s Strategy instance to use
     *
     * @return Builder instance itself to allow chaining
     */
    public B enumNamingStrategy(EnumNamingStrategy s) {
        _baseSettings = _baseSettings.with(s);
        return _this();
    }

    /**
     * Method for configuring {@link AccessorNamingStrategy} to use for auto-detecting
     * accessor ("getter") and mutator ("setter") methods based on naming of methods.
     *
     * @param s Strategy instance to use; if null, use the default implementation
     *
     * @return Builder instance itself to allow chaining
     */
    public B accessorNaming(AccessorNamingStrategy.Provider s) {
        if (s == null) {
            s = new DefaultAccessorNamingStrategy.Provider();
        }
        _baseSettings = _baseSettings.with(s);
        return _this();
    }

    /*
    /**********************************************************************
    /* Changing factories, serialization
    /**********************************************************************
     */

    public B serializerFactory(SerializerFactory f) {
        _serializerFactory = f;
        return _this();
    }

    public B serializationContexts(SerializationContexts ctxt) {
        _serializationContexts = ctxt;
        return _this();
    }

    /**
     * Method for configuring this mapper to use specified {@link FilterProvider} for
     * mapping Filter Ids to actual filter instances.
     *<p>
     * Note that usually it is better to use method in {@link ObjectWriter}, but sometimes
     * this method is more convenient. For example, some frameworks only allow configuring
     * of ObjectMapper instances and not {@link ObjectWriter}s.
     */
    public B filterProvider(FilterProvider prov) {
        _filterProvider = prov;
        return _this();
    }

    public B defaultPrettyPrinter(PrettyPrinter pp) {
        _defaultPrettyPrinter = pp;
        return _this();
    }

    /*
    /**********************************************************************
    /* Changing factories, related, deserialization
    /**********************************************************************
     */

    public B deserializerFactory(DeserializerFactory f) {
        _deserializerFactory = f;
        return _this();
    }

    public B deserializationContexts(DeserializationContexts ctxt) {
        _deserializationContexts = ctxt;
        return _this();
    }

    public B injectableValues(InjectableValues v) {
        _injectableValues = v;
        return _this();
    }

    public B nodeFactory(JsonNodeFactory f) {
        _baseSettings = _baseSettings.with(f);
        return _this();
    }

    /**
     * Method for specifying {@link ConstructorDetector} to use for
     * determining some aspects of creator auto-detection (specifically
     * auto-detection of constructor, and in particular behavior with
     * single-argument constructors).
     */
    public B constructorDetector(ConstructorDetector cd) {
        _baseSettings = _baseSettings.with(cd);
        return _this();
    }

    public B cacheProvider(CacheProvider cacheProvider) {
        _baseSettings = _baseSettings.with(cacheProvider);
        // Unlike Deserializer-/SerializerCaches, need to eagerly update
        // TypeFactory now. Need to call `typeFactory()` to force instantiation
        TypeFactory tf = typeFactory().withCache(cacheProvider.forTypeFactory());
        return typeFactory(tf);
    }

    /**
     * Method used for adding a {@link DeserializationProblemHandler} for this
     * builder, at the head of the list (meaning it has priority over handler
     * registered earlier).
     */
    public B addHandler(DeserializationProblemHandler h) {
        if (!LinkedNode.contains(_problemHandlers, h)) {
            _problemHandlers = new LinkedNode<>(h, _problemHandlers);
        }
        return _this();
    }

    /**
     * Method that may be used to remove all {@link DeserializationProblemHandler}s added
     * to this builder (if any).
     */
    public B clearProblemHandlers() {
        _problemHandlers = null;
        return _this();
    }

    /**
     * Method for inserting specified {@link AbstractTypeResolver} as the first resolver
     * in chain of possibly multiple resolvers.
     */
    public B addAbstractTypeResolver(AbstractTypeResolver resolver) {
        if (resolver == null) {
            throw new IllegalArgumentException("Cannot pass null resolver");
        }
        _abstractTypeResolvers = ArrayBuilders.insertInListNoDup(_abstractTypeResolvers, resolver);
        return _this();
    }

    /*
    /**********************************************************************
    /* Changing settings, date/time
    /**********************************************************************
     */

    /**
     * Method for configuring the default {@link DateFormat} to use when serializing time
     * values as Strings, and deserializing from JSON Strings.
     * If you need per-request configuration, factory methods in
     * {@link ObjectReader} and {@link ObjectWriter} instead.
     */
    public B defaultDateFormat(DateFormat f) {
        _baseSettings = _baseSettings.with(f);
        configure(DateTimeFeature.WRITE_DATES_AS_TIMESTAMPS, (f == null));
        return _this();
    }

    /**
     * Method for overriding default TimeZone to use for formatting.
     * Default value used is UTC (NOT default TimeZone of JVM).
     */
    public B defaultTimeZone(TimeZone tz) {
        _baseSettings = _baseSettings.with(tz);
        return _this();
    }

    /**
     * Method for overriding default locale to use for formatting.
     * Default value used is {@link Locale#getDefault()}.
     */
    public B defaultLocale(Locale locale) {
        _baseSettings = _baseSettings.with(locale);
        return _this();
    }

    /*
    /**********************************************************************
    /* Changing settings, formatting
    /**********************************************************************
     */

    /**
     * Method that will configure default {@link Base64Variant} that
     * <code>byte[]</code> serializers and deserializers will use.
     *
     * @param v Base64 variant to use
     *
     * @return This mapper, for convenience to allow chaining
     */
    public B defaultBase64Variant(Base64Variant v) {
        _baseSettings = _baseSettings.with(v);
        return _this();
    }

    /*
    /**********************************************************************
    /* Configuring Mix-ins
    /**********************************************************************
     */

    /**
     * Method that may be used to completely change mix-in handling by providing
     * alternate {@link MixInHandler} implementation.
     * Most of the time this is NOT the method you want to call, and rather are looking
     * for {@link #mixInOverrides}.
     */
    public B mixInHandler(MixInHandler h) {
        _mixInHandler = h;
        return _this();
    }

    /**
     * Method that allows defining "override" mix-in resolver: something that is checked first,
     * before simple mix-in definitions.
     */
    public B mixInOverrides(MixInResolver r) {
        _mixInHandler = mixInHandler().withOverrides(r);
        return _this();
    }

    /**
     * Method to use for defining mix-in annotations to use for augmenting
     * annotations that processable (serializable / deserializable)
     * classes have.
     * This convenience method is equivalent to iterating over all entries
     * and calling {@link #addMixIn} with `key` and `value` of each entry.
     */
    public B addMixIns(Map<Class<?>, Class<?>> sourceMixins)
    {
        mixInHandler().addLocalDefinitions(sourceMixins);
        return _this();
    }

    /**
     * Method to use for defining mix-in annotations to use for augmenting
     * annotations that classes have, for purpose of configuration serialization
     * and/or deserialization processing.
     * Mixing in is done when introspecting class annotations and properties.
     * Annotations from "mixin" class (and its supertypes)
     * will <b>override</b>
     * annotations that target classes (and their super-types) have.
     *<p>
     * Note that standard mixin handler implementations will only allow a single mix-in
     * source class per target, so if there was a previous mix-in defined target it will
     * be cleared. This also means that you can remove mix-in definition by specifying
     * {@code mixinSource} of {@code null}
     * (although preferred mechanism is {@link #removeMixIn})
     *
     * @param target Target class on which to add annotations
     * @param mixinSource Class that has annotations to add
     *
     * @return This builder instance to allow call chaining
     */
    public B addMixIn(Class<?> target, Class<?> mixinSource)
    {
        mixInHandler().addLocalDefinition(target, mixinSource);
        return _this();
    }

    /**
     * Method that allows making sure that specified {@code target} class
     * does not have associated mix-in annotations: basically can be used
     * to undo an earlier call to {@link #addMixIn}.
     *<p>
     * NOTE: removing mix-ins for given class does not try to remove possible
     * mix-ins for any of its super classes and super interfaces; only direct
     * mix-in addition, if any, is removed.
     *
     * @param target Target class for which no mix-ins should remain after call
     *
     * @return This builder instance to allow call chaining
     */
    public B removeMixIn(Class<?> target)
    {
        mixInHandler().addLocalDefinition(target, null);
        return _this();
    }

    /*
    /**********************************************************************
    /* Subtype registration
    /**********************************************************************
     */

    public B registerSubtypes(Class<?>... subtypes) {
        subtypeResolver().registerSubtypes(subtypes);
        return _this();
    }

    public B registerSubtypes(NamedType... subtypes) {
        subtypeResolver().registerSubtypes(subtypes);
        return _this();
    }

    public B registerSubtypes(Collection<Class<?>> subtypes) {
        subtypeResolver().registerSubtypes(subtypes);
        return _this();
    }

    /*
    /**********************************************************************
    /* Default typing (temporarily)
    /**********************************************************************
     */

    /**
     * Convenience method that is equivalent to calling
     *<pre>
     *  activateDefaultTyping(DefaultTyping.OBJECT_AND_NON_CONCRETE);
     *</pre>
     *<p>
     * NOTE: choice of {@link PolymorphicTypeValidator} to configure is of
     * crucial importance to security when deserializing untrusted content:
     * this because allowing deserializing of any type can lead to malicious
     * attacks using "deserialization gadgets". Implementations should use
     * allow-listing to specify acceptable types unless source of content
     * is fully trusted to only send safe types.
     */
    public B activateDefaultTyping(PolymorphicTypeValidator subtypeValidator) {
        return activateDefaultTyping(subtypeValidator, DefaultTyping.OBJECT_AND_NON_CONCRETE);
    }

    /**
     * Convenience method that is equivalent to calling
     *<pre>
     *  activateDefaultTyping(dti, JsonTypeInfo.As.WRAPPER_ARRAY);
     *</pre>
     *<p>
     * NOTE: choice of {@link PolymorphicTypeValidator} to configure is of
     * crucial importance to security when deserializing untrusted content:
     * this because allowing deserializing of any type can lead to malicious
     * attacks using "deserialization gadgets". Implementations should use
     * allow-listing to specify acceptable types unless source of content
     * is fully trusted to only send safe types.
     */
    public B activateDefaultTyping(PolymorphicTypeValidator subtypeValidator,
            DefaultTyping dti) {
        return activateDefaultTyping(subtypeValidator,
                dti, JsonTypeInfo.As.WRAPPER_ARRAY);
    }

    /**
     * Method for enabling automatic inclusion of type information, needed
     * for proper deserialization of polymorphic types (unless types
     * have been annotated with {@link com.fasterxml.jackson.annotation.JsonTypeInfo}).
     *<P>
     * NOTE: use of <code>JsonTypeInfo.As#EXTERNAL_PROPERTY</code> <b>NOT SUPPORTED</b>;
     * and attempts of do so will throw an {@link IllegalArgumentException} to make
     * this limitation explicit.
     *<p>
     * NOTE: choice of {@link PolymorphicTypeValidator} to configure is of
     * crucial importance to security when deserializing untrusted content:
     * this because allowing deserializing of any type can lead to malicious
     * attacks using "deserialization gadgets". Implementations should use
     * allow-listing to specify acceptable types unless source of content
     * is fully trusted to only send safe types.
     *
     * @param applicability Defines kinds of types for which additional type information
     *    is added; see {@link DefaultTyping} for more information.
     */
    public B activateDefaultTyping(PolymorphicTypeValidator subtypeValidator,
            DefaultTyping applicability, JsonTypeInfo.As includeAs)
    {
        // Use if "As.EXTERNAL_PROPERTY" will not work, check to ensure no attempts made
        if (includeAs == JsonTypeInfo.As.EXTERNAL_PROPERTY) {
            throw new IllegalArgumentException("Cannot use includeAs of "+includeAs+" for Default Typing");
        }
        return setDefaultTyping(_defaultDefaultTypingResolver(subtypeValidator,
                applicability, includeAs));
    }

    /**
     * Method for enabling automatic inclusion of type information -- needed
     * for proper deserialization of polymorphic types (unless types
     * have been annotated with {@link com.fasterxml.jackson.annotation.JsonTypeInfo}) --
     * using "As.PROPERTY" inclusion mechanism and specified property name
     * to use for inclusion (default being "@class" since default type information
     * always uses class name as type identifier)
     *<p>
     * NOTE: choice of {@link PolymorphicTypeValidator} to configure is of
     * crucial importance to security when deserializing untrusted content:
     * this because allowing deserializing of any type can lead to malicious
     * attacks using "deserialization gadgets". Implementations should use
     * allow-listing to specify acceptable types unless source of content
     * is fully trusted to only send safe types.
     */
    public B activateDefaultTypingAsProperty(PolymorphicTypeValidator subtypeValidator,
            DefaultTyping applicability, String propertyName)
    {
        return setDefaultTyping(_defaultDefaultTypingResolver(subtypeValidator,
                applicability, propertyName));
    }

    /**
     * Method for disabling automatic inclusion of type information; if so, only
     * explicitly annotated types (ones with
     * {@link com.fasterxml.jackson.annotation.JsonTypeInfo}) will have
     * additional embedded type information.
     */
    public B deactivateDefaultTyping() {
        return setDefaultTyping(null);
    }

    /**
     * Method for enabling automatic inclusion of type information, using
     * specified handler object for determining which types this affects,
     * as well as details of how information is embedded.
     *<p>
     * NOTE: use of Default Typing can be a potential security risk if incoming
     * content comes from untrusted sources, so care should be taken to use
     * a {@link TypeResolverBuilder} that can limit allowed classes to
     * deserialize.
     *
     * @param typer Type information inclusion handler
     */
    public B setDefaultTyping(TypeResolverBuilder<?> typer) {
        _baseSettings = _baseSettings.with(typer);
        return _this();
    }

    /**
     * Overridable method for changing default {@link TypeResolverBuilder} to construct
     * for "default typing".
     */
    protected TypeResolverBuilder<?> _defaultDefaultTypingResolver(PolymorphicTypeValidator subtypeValidator,
            DefaultTyping applicability, JsonTypeInfo.As includeAs) {
        return new DefaultTypeResolverBuilder(subtypeValidator, applicability, includeAs);
    }

    /**
     * Overridable method for changing default {@link TypeResolverBuilder} to construct
     * for "default typing".
     */
    protected TypeResolverBuilder<?> _defaultDefaultTypingResolver(PolymorphicTypeValidator subtypeValidator,
            DefaultTyping applicability, String propertyName) {
        return new DefaultTypeResolverBuilder(subtypeValidator, applicability, propertyName);
    }

    /*
    /**********************************************************************
    /* Other helper methods
    /**********************************************************************
     */

    // silly convenience cast method we need
    @SuppressWarnings("unchecked")
    protected final B _this() { return (B) this; }
}