BaseSettings.java

package tools.jackson.databind.cfg;

import java.text.DateFormat;
import java.util.Locale;
import java.util.TimeZone;

import tools.jackson.core.Base64Variant;
import tools.jackson.databind.*;
import tools.jackson.databind.introspect.AccessorNamingStrategy;
import tools.jackson.databind.introspect.AnnotationIntrospectorPair;
import tools.jackson.databind.jsontype.PolymorphicTypeValidator;
import tools.jackson.databind.jsontype.TypeResolverBuilder;
import tools.jackson.databind.node.JsonNodeFactory;
import tools.jackson.databind.util.StdDateFormat;

/**
 * Immutable container class used to store simple configuration
 * settings for both serialization and deserialization.
 * Since instances are fully immutable, instances can
 * be freely shared and used without synchronization.
 */
public final class BaseSettings
    implements java.io.Serializable
{
    private static final long serialVersionUID = 1L;

    /**
     * We will use a default TimeZone as the baseline.
     */
    private static final TimeZone DEFAULT_TIMEZONE = TimeZone.getTimeZone("UTC");

    /*
    /**********************************************************************
    /* Configuration settings; introspection, related
    /**********************************************************************
     */

    /**
     * Introspector used for accessing annotation value based configuration.
     */
    protected final AnnotationIntrospector _annotationIntrospector;

    /**
     * Custom property naming strategy in use, if any.
     */
    protected final PropertyNamingStrategy _propertyNamingStrategy;

    /**
     * Custom enum naming strategy in use, if any.
     */
    protected final EnumNamingStrategy _enumNamingStrategy;

    /**
     * Provider for creating {@link AccessorNamingStrategy} instances to use
     */
    protected final AccessorNamingStrategy.Provider _accessorNaming;

    /*
    /**********************************************************************
    /* Configuration settings; polymorphic type resolution
    /**********************************************************************
     */

    /**
     * Type information handler used for "default typing".
     */
    protected final TypeResolverBuilder<?> _defaultTyper;

    /**
     * Validator that is used to limit allowed subtypes during polymorphic
     * deserialization,
     * mostly for security reasons when dealing with untrusted content.
     */
    protected final PolymorphicTypeValidator _typeValidator;

    /*
    /**********************************************************************
    /* Configuration settings; other
    /**********************************************************************
     */

    /**
     * Custom date format to use for deserialization. If specified, will be
     * used instead of {@link tools.jackson.databind.util.StdDateFormat}.
     *<p>
     * Note that the configured format object will be cloned once per
     * deserialization process (first time it is needed)
     */
    protected final DateFormat _dateFormat;

    /**
     * Object used for creating instances of handlers (serializers, deserializers,
     * type and type id resolvers), given class to instantiate. This is typically
     * used to do additional configuration (with dependency injection, for example)
     * beyond simply construction of instances; or to use alternative constructors.
     */
    protected final HandlerInstantiator _handlerInstantiator;

    /**
     * Default {@link java.util.Locale} used with serialization formats.
     * Default value is {@link Locale#getDefault()}.
     */
    protected final Locale _locale;

    /**
     * Default {@link java.util.TimeZone} used with serialization formats,
     * if (and only if!) explicitly set by use; otherwise `null` to indicate
     * "use default", which means "UTC" (from Jackson 2.7); earlier versions
     * (up to 2.6) used "GMT".
     *<p>
     * Note that if a new value is set, timezone is also assigned to
     * {@link #_dateFormat} of this object.
     */
    protected final TimeZone _timeZone;

    /**
     * Explicitly default {@link Base64Variant} to use for handling
     * binary data (<code>byte[]</code>), used with data formats
     * that use base64 encoding (like JSON, CSV).
     */
    protected final Base64Variant _defaultBase64;

    /**
     * Used to provide custom cache implementation in downstream components.
     */
    protected final CacheProvider _cacheProvider;

    /**
     * Factory used for constructing {@link tools.jackson.databind.JsonNode} instances.
     */
    protected final JsonNodeFactory _nodeFactory;

    /**
     * Handler that specifies some aspects of Constructor auto-detection.
     */
    protected final ConstructorDetector _ctorDetector;
    
    /*
    /**********************************************************************
    /* Construction
    /**********************************************************************
     */

    public BaseSettings(AnnotationIntrospector ai,
            PropertyNamingStrategy pns, EnumNamingStrategy ens,
            AccessorNamingStrategy.Provider accNaming,
            TypeResolverBuilder<?> defaultTyper, PolymorphicTypeValidator ptv,
            DateFormat dateFormat, HandlerInstantiator hi,
            Locale locale, TimeZone tz, Base64Variant defaultBase64,
            CacheProvider cacheProvider, JsonNodeFactory nodeFactory,
            ConstructorDetector ctorDetector)
    {
        _annotationIntrospector = ai;
        _propertyNamingStrategy = pns;
        _enumNamingStrategy = ens;
        _accessorNaming = accNaming;
        _defaultTyper = defaultTyper;
        _typeValidator = ptv;
        _dateFormat = dateFormat;
        _handlerInstantiator = hi;
        _locale = locale;
        _timeZone = tz;
        _defaultBase64 = defaultBase64;
        _cacheProvider = cacheProvider;
        _nodeFactory = nodeFactory;
        _ctorDetector = ctorDetector;
    }

    /*
    /**********************************************************************
    /* Factory methods
    /**********************************************************************
     */

    public BaseSettings withAnnotationIntrospector(AnnotationIntrospector ai) {
        if (_annotationIntrospector == ai) {
            return this;
        }
        return new BaseSettings(ai, _propertyNamingStrategy, _enumNamingStrategy, _accessorNaming,
                _defaultTyper, _typeValidator, _dateFormat, _handlerInstantiator, _locale,
                _timeZone, _defaultBase64, _cacheProvider, _nodeFactory, _ctorDetector);
    }

    public BaseSettings withInsertedAnnotationIntrospector(AnnotationIntrospector ai) {
        return withAnnotationIntrospector(AnnotationIntrospectorPair.create(ai, _annotationIntrospector));
    }

    public BaseSettings withAppendedAnnotationIntrospector(AnnotationIntrospector ai) {
        return withAnnotationIntrospector(AnnotationIntrospectorPair.create(_annotationIntrospector, ai));
    }

    public BaseSettings with(PropertyNamingStrategy pns) {
        if (_propertyNamingStrategy == pns) {
            return this;
        }
        return new BaseSettings(_annotationIntrospector, pns, _enumNamingStrategy, _accessorNaming,
                _defaultTyper, _typeValidator, _dateFormat, _handlerInstantiator, _locale,
                _timeZone, _defaultBase64, _cacheProvider, _nodeFactory, _ctorDetector);
    }

    public BaseSettings with(EnumNamingStrategy ens) {
        if (_enumNamingStrategy == ens) {
            return this;
        }
        return new BaseSettings(_annotationIntrospector, _propertyNamingStrategy, ens, _accessorNaming,
                _defaultTyper, _typeValidator, _dateFormat, _handlerInstantiator, _locale,
                _timeZone, _defaultBase64, _cacheProvider, _nodeFactory, _ctorDetector);
    }

    public BaseSettings with(AccessorNamingStrategy.Provider p) {
        if (_accessorNaming == p) {
            return this;
        }
        return new BaseSettings(_annotationIntrospector, _propertyNamingStrategy, _enumNamingStrategy, p,
                _defaultTyper, _typeValidator, _dateFormat, _handlerInstantiator, _locale,
                _timeZone, _defaultBase64, _cacheProvider, _nodeFactory, _ctorDetector);
    }

    public BaseSettings with(TypeResolverBuilder<?> typer) {
        if (_defaultTyper == typer) {
            return this;
        }
        return new BaseSettings(_annotationIntrospector, _propertyNamingStrategy, _enumNamingStrategy,
                _accessorNaming,
                typer, _typeValidator, _dateFormat, _handlerInstantiator, _locale,
                _timeZone, _defaultBase64, _cacheProvider, _nodeFactory, _ctorDetector);
    }

    public BaseSettings with(PolymorphicTypeValidator ptv) {
        if (_typeValidator == ptv) {
            return this;
        }
        return new BaseSettings(_annotationIntrospector, _propertyNamingStrategy, _enumNamingStrategy,
                _accessorNaming,
                _defaultTyper, ptv, _dateFormat, _handlerInstantiator, _locale,
                _timeZone, _defaultBase64, _cacheProvider, _nodeFactory, _ctorDetector);
    }

    public BaseSettings with(DateFormat df) {
        if (_dateFormat == df) {
            return this;
        }
        // 26-Sep-2015, tatu: Related to [databind#939], let's try to force TimeZone if
        //   (but only if!) it has been set explicitly.
        if ((df != null) && hasExplicitTimeZone()) {
            df = _force(df, _timeZone);
        }
        return new BaseSettings(_annotationIntrospector, _propertyNamingStrategy, _enumNamingStrategy,
                _accessorNaming,
                _defaultTyper, _typeValidator, df, _handlerInstantiator, _locale,
                _timeZone, _defaultBase64, _cacheProvider, _nodeFactory, _ctorDetector);
    }

    public BaseSettings with(HandlerInstantiator hi) {
        if (_handlerInstantiator == hi) {
            return this;
        }
        return new BaseSettings(_annotationIntrospector, _propertyNamingStrategy, _enumNamingStrategy,
                _accessorNaming,
                _defaultTyper, _typeValidator, _dateFormat, hi, _locale,
                _timeZone, _defaultBase64, _cacheProvider, _nodeFactory, _ctorDetector);
    }

    public BaseSettings with(Locale l) {
        if (_locale == l) {
            return this;
        }
        return new BaseSettings(_annotationIntrospector, _propertyNamingStrategy, _enumNamingStrategy,
                _accessorNaming,
                _defaultTyper, _typeValidator, _dateFormat, _handlerInstantiator, l,
                _timeZone, _defaultBase64, _cacheProvider, _nodeFactory, _ctorDetector);
    }

    /**
     * Fluent factory for constructing a new instance that uses specified TimeZone.
     * Note that timezone used with also be assigned to configured {@link DateFormat},
     * changing time formatting defaults.
     */
    public BaseSettings with(TimeZone tz)
    {
        if (tz == _timeZone) {
            return this;
        }
        DateFormat df = _force(_dateFormat, (tz == null) ? DEFAULT_TIMEZONE : tz);
        return new BaseSettings(_annotationIntrospector, _propertyNamingStrategy, _enumNamingStrategy,
                _accessorNaming,
                _defaultTyper, _typeValidator, df, _handlerInstantiator, _locale,
                tz, _defaultBase64, _cacheProvider, _nodeFactory, _ctorDetector);
    }

    public BaseSettings with(Base64Variant base64) {
        if (base64 == _defaultBase64) {
            return this;
        }
        return new BaseSettings(_annotationIntrospector, _propertyNamingStrategy, _enumNamingStrategy,
                _accessorNaming,
                _defaultTyper, _typeValidator, _dateFormat, _handlerInstantiator, _locale,
                _timeZone, base64, _cacheProvider, _nodeFactory, _ctorDetector);
    }

    /**
     * Fluent factory for constructing a new instance with provided {@link CacheProvider}.
     *
     * @return a new instance with provided {@link CacheProvider}.
     */
    public BaseSettings with(CacheProvider cacheProvider) {
        if (cacheProvider == _cacheProvider) {
            return this;
        }
        return new BaseSettings(_annotationIntrospector, _propertyNamingStrategy, _enumNamingStrategy,
                _accessorNaming,
                _defaultTyper, _typeValidator, _dateFormat, _handlerInstantiator, _locale,
                _timeZone, _defaultBase64, cacheProvider, _nodeFactory, _ctorDetector);
    }

    public BaseSettings with(JsonNodeFactory nodeFactory) {
        if (nodeFactory == _nodeFactory) {
            return this;
        }
        return new BaseSettings(_annotationIntrospector, _propertyNamingStrategy, _enumNamingStrategy,
                _accessorNaming,
                _defaultTyper, _typeValidator, _dateFormat, _handlerInstantiator, _locale,
                _timeZone, _defaultBase64, _cacheProvider, nodeFactory, _ctorDetector);
    }

    public BaseSettings with(ConstructorDetector ctorDetector) {
        if (ctorDetector == _ctorDetector) {
            return this;
        }
        return new BaseSettings(_annotationIntrospector, _propertyNamingStrategy, _enumNamingStrategy,
                _accessorNaming,
                _defaultTyper, _typeValidator, _dateFormat, _handlerInstantiator, _locale,
                _timeZone, _defaultBase64, _cacheProvider, _nodeFactory, ctorDetector);
    }

    /*
    /**********************************************************************
    /* API
    /**********************************************************************
     */

    public AnnotationIntrospector getAnnotationIntrospector() {
        return _annotationIntrospector;
    }

    public PropertyNamingStrategy getPropertyNamingStrategy() {
        return _propertyNamingStrategy;
    }

    public EnumNamingStrategy getEnumNamingStrategy() {
        return _enumNamingStrategy;
    }

    public AccessorNamingStrategy.Provider getAccessorNaming() {
        return _accessorNaming;
    }

    public TypeResolverBuilder<?> getDefaultTyper() {
        return _defaultTyper;
    }

    public PolymorphicTypeValidator getPolymorphicTypeValidator() {
        return _typeValidator;
    }

    public DateFormat getDateFormat() {
        return _dateFormat;
    }

    public HandlerInstantiator getHandlerInstantiator() {
        return _handlerInstantiator;
    }

    public Locale getLocale() {
        return _locale;
    }

    public TimeZone getTimeZone() {
        TimeZone tz = _timeZone;
        return (tz == null) ? DEFAULT_TIMEZONE : tz;
    }

    /**
     * Accessor that may be called to determine whether this settings object
     * has been explicitly configured with a TimeZone (true), or is still
     * relying on the default settings (false).
     */
    public boolean hasExplicitTimeZone() {
        return (_timeZone != null);
    }

    public Base64Variant getBase64Variant() {
        return _defaultBase64;
    }

    public CacheProvider getCacheProvider() {
        return _cacheProvider;
    }

    public JsonNodeFactory getNodeFactory() {
        return _nodeFactory;
    }

    public ConstructorDetector getConstructorDetector() {
        return (_ctorDetector == null) ? ConstructorDetector.DEFAULT : _ctorDetector;
    }

    /*
    /**********************************************************************
    /* Helper methods
    /**********************************************************************
     */

    private DateFormat _force(DateFormat df, TimeZone tz)
    {
        if (df instanceof StdDateFormat sdf) {
            return sdf.withTimeZone(tz);
        }
        // we don't know if original format might be shared; better create a clone:
        df = (DateFormat) df.clone();
        df.setTimeZone(tz);
        return df;
    }
}