MapperConfigBase.java

package tools.jackson.databind.cfg;

import java.text.DateFormat;
import java.util.*;

import com.fasterxml.jackson.annotation.*;

import tools.jackson.core.Base64Variant;
import tools.jackson.core.type.TypeReference;
import tools.jackson.databind.*;
import tools.jackson.databind.introspect.*;
import tools.jackson.databind.jsontype.SubtypeResolver;
import tools.jackson.databind.jsontype.TypeResolverBuilder;
import tools.jackson.databind.jsontype.TypeResolverProvider;
import tools.jackson.databind.node.JsonNodeFactory;
import tools.jackson.databind.type.TypeFactory;
import tools.jackson.databind.util.ClassUtil;
import tools.jackson.databind.util.Converter;
import tools.jackson.databind.util.RootNameLookup;

@SuppressWarnings("serial")
public abstract class MapperConfigBase<CFG extends ConfigFeature,
    T extends MapperConfigBase<CFG,T>>
    extends MapperConfig<T>
    implements java.io.Serializable
{
    protected final static ConfigOverride EMPTY_OVERRIDE = ConfigOverride.empty();

    /*
    /**********************************************************************
    /* Immutable config, factories
    /**********************************************************************
     */

    /**
     * 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 final TypeFactory _typeFactory;

    protected final ClassIntrospector _classIntrospector;

    /**
     * @since 3.0
     */
    protected final TypeResolverProvider _typeResolverProvider;

    /**
     * Registered concrete subtypes that can be used instead of (or
     * in addition to) ones declared using annotations.
     *<p>
     * Note that instances are stateful and as such may need to be copied,
     * and may NOT be demoted down to {@link BaseSettings}.
     */
    protected final SubtypeResolver _subtypeResolver;

    /**
     * Mix-in annotation mappings to use, if any.
     */
    protected final MixInHandler _mixIns;

    /*
    /**********************************************************************
    /* Immutable config, factories
    /**********************************************************************
     */

    /**
     * Explicitly defined root name to use, if any; if empty
     * String, will disable root-name wrapping; if null, will
     * use defaults
     */
    protected final PropertyName _rootName;

    /**
     * View to use for filtering out properties to serialize
     * or deserialize.
     * Null if none (will also be assigned null if <code>Object.class</code>
     * is defined), meaning that all properties are to be included.
     */
    protected final Class<?> _view;

    /**
     * Contextual attributes accessible (get and set) during processing,
     * on per-call basis.
     */
    protected final ContextAttributes _attributes;

    /**
     * Simple cache used for finding out possible root name for root name
     * wrapping.
     *<p>
     * Note that instances are stateful (for caching) and as such may need to be copied,
     * and may NOT be demoted down to {@link BaseSettings}.
     */
    protected final RootNameLookup _rootNames;

    /**
     * Configuration overrides to apply, keyed by type of property.
     */
    protected final ConfigOverrides _configOverrides;

    /**
     * Set of {@link DatatypeFeature}s enabled.
     */
    protected final DatatypeFeatures _datatypeFeatures;

    /*
    /**********************************************************************
    /* Construction
    /**********************************************************************
     */

    /**
     * Constructor used when creating a new instance (compared to
     * that of creating fluent copies)
     */
    protected MapperConfigBase(MapperBuilder<?,?> b, long mapperFeatures,
            TypeFactory tf, ClassIntrospector classIntr, MixInHandler mixins, SubtypeResolver str,
            ConfigOverrides configOverrides, ContextAttributes defaultAttrs,
            RootNameLookup rootNames)
    {
        super(b.baseSettings(), mapperFeatures);

        _typeFactory = tf;
        _classIntrospector = classIntr;
        _typeResolverProvider = b.typeResolverProvider();
        _subtypeResolver = str;
        _mixIns = mixins;
        _rootNames = rootNames;
        _rootName = null;
        _view = null;
        _attributes = defaultAttrs;
        _configOverrides = configOverrides;
        _datatypeFeatures = b.datatypeFeatures();
    }

    /**
     * Pass-through constructor used when no changes are needed to the
     * base class.
     */
    protected MapperConfigBase(MapperConfigBase<CFG,T> src)
    {
        super(src);
        _typeFactory = src._typeFactory;
        _classIntrospector = src._classIntrospector;
        _typeResolverProvider = src._typeResolverProvider;
        _subtypeResolver = src._subtypeResolver;

        _mixIns = src._mixIns;
        _rootNames = src._rootNames;
        _rootName = src._rootName;
        _view = src._view;
        _attributes = src._attributes;
        _configOverrides = src._configOverrides;
        _datatypeFeatures = src._datatypeFeatures;
    }

    protected MapperConfigBase(MapperConfigBase<CFG,T> src, BaseSettings base)
    {
        super(src, base);
        _typeFactory = src._typeFactory;
        _classIntrospector = src._classIntrospector;
        _typeResolverProvider = src._typeResolverProvider;
        _subtypeResolver = src._subtypeResolver;
        _mixIns = src._mixIns;
        _rootNames = src._rootNames;
        _rootName = src._rootName;
        _view = src._view;
        _attributes = src._attributes;
        _configOverrides = src._configOverrides;
        _datatypeFeatures = src._datatypeFeatures;
    }

    protected MapperConfigBase(MapperConfigBase<CFG,T> src, PropertyName rootName) {
        super(src);
        _typeFactory = src._typeFactory;
        _classIntrospector = src._classIntrospector;
        _typeResolverProvider = src._typeResolverProvider;
        _subtypeResolver = src._subtypeResolver;

        _mixIns = src._mixIns;
        _rootNames = src._rootNames;
        _rootName = rootName;
        _view = src._view;
        _attributes = src._attributes;
        _configOverrides = src._configOverrides;
        _datatypeFeatures = src._datatypeFeatures;
    }

    protected MapperConfigBase(MapperConfigBase<CFG,T> src, Class<?> view)
    {
        super(src);
        _typeFactory = src._typeFactory;
        _classIntrospector = src._classIntrospector;
        _typeResolverProvider = src._typeResolverProvider;
        _subtypeResolver = src._subtypeResolver;

        _mixIns = src._mixIns;
        _rootNames = src._rootNames;
        _rootName = src._rootName;
        _view = view;
        _attributes = src._attributes;
        _configOverrides = src._configOverrides;
        _datatypeFeatures = src._datatypeFeatures;
    }

    protected MapperConfigBase(MapperConfigBase<CFG,T> src, ContextAttributes attr)
    {
        super(src);
        _typeFactory = src._typeFactory;
        _classIntrospector = src._classIntrospector;
        _typeResolverProvider = src._typeResolverProvider;
        _subtypeResolver = src._subtypeResolver;

        _mixIns = src._mixIns;
        _rootNames = src._rootNames;
        _rootName = src._rootName;
        _view = src._view;
        _attributes = attr;
        _configOverrides = src._configOverrides;
        _datatypeFeatures = src._datatypeFeatures;
    }

    protected MapperConfigBase(MapperConfigBase<CFG,T> src, DatatypeFeatures datatypeFeatures)
    {
        super(src);
        _typeFactory = src._typeFactory;
        _classIntrospector = src._classIntrospector;
        _typeResolverProvider = src._typeResolverProvider;
        _subtypeResolver = src._subtypeResolver;

        _mixIns = src._mixIns;
        _rootNames = src._rootNames;
        _rootName = src._rootName;
        _view = src._view;
        _attributes = src._attributes;
        _configOverrides = src._configOverrides;

        _datatypeFeatures = datatypeFeatures;
    }

    /*
    /**********************************************************************
    /* Abstract fluent factory methods to be implemented by subtypes
    /**********************************************************************
     */

    protected abstract T _withBase(BaseSettings newBase);

    protected abstract T _with(DatatypeFeatures dtFeatures);

    protected DatatypeFeatures _datatypeFeatures() {
        return _datatypeFeatures;
    }

    /*
    /**********************************************************************
    /* Additional shared fluent factory methods; DatatypeFeatures
    /**********************************************************************
     */

    /**
     * Fluent factory method that will return a configuration
     * object instance with specified feature enabled: this may be
     * {@code this} instance (if no changes effected), or a newly
     * constructed instance.
     */
    public final T with(DatatypeFeature feature) {
        return _with(_datatypeFeatures().with(feature));
    }

    /**
     * Fluent factory method that will return a configuration
     * object instance with specified features enabled: this may be
     * {@code this} instance (if no changes effected), or a newly
     * constructed instance.
     */
    public final T withFeatures(DatatypeFeature... features) {
        return _with(_datatypeFeatures().withFeatures(features));
    }

    /**
     * Fluent factory method that will return a configuration
     * object instance with specified feature disabled: this may be
     * {@code this} instance (if no changes effected), or a newly
     * constructed instance.
     */
    public final T without(DatatypeFeature feature) {
        return _with(_datatypeFeatures().without(feature));
    }

    /**
     * Fluent factory method that will return a configuration
     * object instance with specified features disabled: this may be
     * {@code this} instance (if no changes effected), or a newly
     * constructed instance.
     */
    public final T withoutFeatures(DatatypeFeature... features) {
        return _with(_datatypeFeatures().withoutFeatures(features));
    }

    /**
     * Fluent factory method that will construct and return a new configuration
     * object instance with specified features disabled.
     */

    public final T with(DatatypeFeature feature, boolean state)
    {
        DatatypeFeatures features = _datatypeFeatures();
        features = state ? features.with(feature) : features.without(feature);
        return _with(features);
    }

    /*
    /**********************************************************************
    /* Additional shared fluent factory methods; attributes
    /**********************************************************************
     */

    /**
     * Method for constructing an instance that has specified
     * contextual attributes.
     */
    public abstract T with(ContextAttributes attrs);

    /**
     * Method for constructing an instance that has only specified
     * attributes, removing any attributes that exist before the call.
     */
    public T withAttributes(Map<?,?> attributes) {
        return with(getAttributes().withSharedAttributes(attributes));
    }

    /**
     * Method for constructing an instance that has specified
     * value for attribute for given key.
     */
    public T withAttribute(Object key, Object value) {
        return with(getAttributes().withSharedAttribute(key, value));
    }

    /**
     * Method for constructing an instance that has no
     * value for attribute for given key.
     */
    public T withoutAttribute(Object key) {
        return with(getAttributes().withoutSharedAttribute(key));
    }

    /*
    /**********************************************************************
    /* Additional shared fluent factory methods; factories
    /**********************************************************************
     */

    /**
     * Method for constructing and returning a new instance with different
     * {@link TypeResolverBuilder} to use.
     */
    public final T with(TypeResolverBuilder<?> trb) {
        return _withBase(_base.with(trb));
    }

    /**
     * @since 2.16
     */
    public T with(CacheProvider provider) {
        return _withBase(_base.with(Objects.requireNonNull(provider)));
    }

    /*
    /**********************************************************************
    /* Additional shared fluent factory methods; other
    /**********************************************************************
     */

    /**
     * Fluent factory method that will construct a new instance with
     * specified {@link JsonNodeFactory}.
     */
    public final T with(JsonNodeFactory f) {
        return _withBase(_base.with(f));
    }

    /**
     * Method for constructing and returning a new instance with different
     * default {@link Base64Variant} to use with base64-encoded binary values.
     */
    public final T with(Base64Variant base64) {
        return _withBase(_base.with(base64));
    }

    /**
     * Method for constructing and returning a new instance with different
     * {@link DateFormat}
     * to use.
     *<p>
     * NOTE: non-final since <code>SerializationConfig</code> needs to override this
     */
    public T with(DateFormat df) {
        return _withBase(_base.with(df));
    }

    /**
     * Method for constructing and returning a new instance with different
     * default {@link java.util.Locale} to use for formatting.
     */
    public final T with(Locale l) {
        return _withBase(_base.with(l));
    }

    /**
     * Method for constructing and returning a new instance with different
     * default {@link java.util.TimeZone} to use for formatting of date values.
     */
    public final T with(TimeZone tz) {
        return _withBase(_base.with(tz));
    }

    /**
     * Method for constructing and returning a new instance with different
     * root name to use (none, if null).
     *<p>
     * Note that when a root name is set to a non-Empty String, this will automatically force use
     * of root element wrapping with given name. If empty String passed, will
     * disable root name wrapping; and if null used, will instead use
     * <code>SerializationFeature</code> to determine if to use wrapping, and annotation
     * (or default name) for actual root name to use.
     *
     * @param rootName to use: if null, means "use default" (clear setting);
     *   if empty String ("") means that no root name wrapping is used;
     *   otherwise defines root name to use.
     */
    public abstract T withRootName(PropertyName rootName);

    public T withRootName(String rootName) {
        if (rootName == null) {
            return withRootName((PropertyName) null);
        }
        return withRootName(PropertyName.construct(rootName));
    }

    /**
     * Method for constructing and returning a new instance with different
     * view to use.
     */
    public abstract T withView(Class<?> view);

    /*
    /**********************************************************************
    /* Simple factory access, related
    /**********************************************************************
     */

    @Override
    public final TypeFactory getTypeFactory() {
        return _typeFactory;
    }

    @Override
    public ClassIntrospector classIntrospectorInstance() {
        return _classIntrospector.forOperation(this);
    }

    @Override
    public TypeResolverProvider getTypeResolverProvider() {
        return _typeResolverProvider;
    }

    /**
     * Accessor for object used for finding out all reachable subtypes
     * for supertypes; needed when a logical type name is used instead
     * of class name (or custom scheme).
     */
    @Override
    public final SubtypeResolver getSubtypeResolver() {
        return _subtypeResolver;
    }

    @Override
    public final JavaType constructType(Class<?> cls) {
        return _typeFactory.constructType(cls);
    }

    @Override
    public final JavaType constructType(TypeReference<?> valueTypeRef) {
        return _typeFactory.constructType(valueTypeRef.getType());
    }

    /*
    /**********************************************************************
    /* Simple Feature access
    /**********************************************************************
     */
    
    @Override
    public final boolean isEnabled(DatatypeFeature feature) {
        return _datatypeFeatures.isEnabled(feature);
    }

    @Override
    public final DatatypeFeatures getDatatypeFeatures() {
        return _datatypeFeatures;
    }
    
    /*
    /**********************************************************************
    /* Simple config property access
    /**********************************************************************
     */

    public final PropertyName getFullRootName() {
        return _rootName;
    }

    @Override
    public final Class<?> getActiveView() {
        return _view;
    }

    @Override
    public final ContextAttributes getAttributes() {
        return _attributes;
    }

    /*
    /**********************************************************************
    /* Configuration access; default/overrides
    /**********************************************************************
     */

    @Override
    public final ConfigOverride getConfigOverride(Class<?> type) {
        ConfigOverride override = _configOverrides.findOverride(type);
        return (override == null) ? EMPTY_OVERRIDE : override;
    }

    @Override
    public final ConfigOverride findConfigOverride(Class<?> type) {
        return _configOverrides.findOverride(type);
    }

    @Override
    public final JsonInclude.Value getDefaultPropertyInclusion() {
        return _configOverrides.getDefaultInclusion();
    }

    @Override
    public final JsonInclude.Value getDefaultPropertyInclusion(Class<?> baseType) {
        JsonInclude.Value v = getConfigOverride(baseType).getInclude();
        JsonInclude.Value def = getDefaultPropertyInclusion();
        if (def == null) {
            return v;
        }
        return def.withOverrides(v);
    }

    @Override
    public final JsonInclude.Value getDefaultInclusion(Class<?> baseType,
            Class<?> propertyType) {
        JsonInclude.Value v = getConfigOverride(propertyType).getIncludeAsProperty();
        JsonInclude.Value def = getDefaultPropertyInclusion(baseType);
        if (def == null) {
            return v;
        }
        return def.withOverrides(v);
    }

    @Override // @since 3.1
    public JsonFormat.Value getDefaultFormat() {
        return _configOverrides.getDefaultFormat();
    }

    @Override
    public final JsonFormat.Value getDefaultPropertyFormat(Class<?> type) {
        return _configOverrides.findFormatDefaults(type);
    }

    @Override
    public final JsonIgnoreProperties.Value getDefaultPropertyIgnorals(Class<?> type) {
        ConfigOverride overrides = _configOverrides.findOverride(type);
        if (overrides != null) {
            JsonIgnoreProperties.Value v = overrides.getIgnorals();
            if (v != null) {
                return v;
            }
        }
        // 01-May-2015, tatu: Could return `Value.empty()` but for now `null`
        //   seems simpler as callers can avoid processing.
        return null;
    }

    @Override
    public final JsonIgnoreProperties.Value getDefaultPropertyIgnorals(Class<?> baseType,
            AnnotatedClass actualClass)
    {
        AnnotationIntrospector intr = getAnnotationIntrospector();
        JsonIgnoreProperties.Value base = (intr == null) ? null
                : intr.findPropertyIgnoralByName(this, actualClass);
        JsonIgnoreProperties.Value overrides = getDefaultPropertyIgnorals(baseType);
        return JsonIgnoreProperties.Value.merge(base, overrides);
    }

    @Override
    public final JsonIncludeProperties.Value getDefaultPropertyInclusions(Class<?> baseType,
            AnnotatedClass actualClass)
    {
        AnnotationIntrospector intr = getAnnotationIntrospector();
        return (intr == null) ? null : intr.findPropertyInclusionByName(this, actualClass);
    }

    @Override
    public final VisibilityChecker getDefaultVisibilityChecker()
    {
        return _configOverrides.getDefaultVisibility();
    }

    @Override
    public final VisibilityChecker getDefaultVisibilityChecker(Class<?> baseType,
            AnnotatedClass actualClass)
    {
        // 14-Apr-2021, tatu: [databind#3117] JDK types should be limited
        //    to "public-only" regardless of settings for other types
        VisibilityChecker vc;

        if (ClassUtil.isJDKClass(baseType)) {
            vc = VisibilityChecker.allPublicInstance();
        } else if (baseType.isRecord()) {
            // 15-Jan-2023, tatu: [databind#3724] Records require slightly different defaults
            vc = _configOverrides.getDefaultRecordVisibility();
        } else {
            vc = getDefaultVisibilityChecker();
        }
        AnnotationIntrospector intr = getAnnotationIntrospector();
        if (intr != null) {
            vc = intr.findAutoDetectVisibility(this, actualClass, vc);
        }
        ConfigOverride overrides = _configOverrides.findOverride(baseType);
        if (overrides != null) {
            vc = vc.withOverrides(overrides.getVisibility()); // ok to pass null
        }
        return vc;
    }

    @Override
    public final JsonSetter.Value getDefaultNullHandling() {
        return _configOverrides.getDefaultNullHandling();
    }

    @Override
    public Boolean getDefaultMergeable() {
        return _configOverrides.getDefaultMergeable();
    }

    @Override
    public Boolean getDefaultMergeable(Class<?> baseType) {
        Boolean b;
        ConfigOverride cfg = _configOverrides.findOverride(baseType);
        if (cfg != null) {
            b = cfg.getMergeable();
            if (b != null) {
                return b;
            }
        }
        return _configOverrides.getDefaultMergeable();
    }

    /*
    /**********************************************************************
    /* Other config access
    /**********************************************************************
     */

    @Override
    public PropertyName findRootName(DatabindContext ctxt, JavaType rootType) {
        if (_rootName != null) {
            return _rootName;
        }
        return _rootNames.findRootName(ctxt, rootType);
    }

    @Override
    public PropertyName findRootName(DatabindContext ctxt, Class<?> rawRootType) {
        if (_rootName != null) {
            return _rootName;
        }
        return _rootNames.findRootName(ctxt, rawRootType);
    }

    /*
    /**********************************************************************
    /* MixInResolver impl:
    /**********************************************************************
     */

    /**
     * Method that will check if there are "mix-in" classes (with mix-in
     * annotations) for given class
     */
    @Override
    public final Class<?> findMixInClassFor(Class<?> cls) {
        return _mixIns.findMixInClassFor(cls);
    }

    @Override
    public boolean hasMixIns() {
        return _mixIns.hasMixIns();
    }

    // Not really relevant here (should not get called)
    @Override
    public MixInResolver snapshot() {
        throw new UnsupportedOperationException();
    }

    /**
     * Test-only method -- does not reflect possibly open-ended set that external
     * mix-in resolver might provide.
     */
    public final int mixInCount() {
        return _mixIns.localSize();
    }

    /*
    /**********************************************************************
    /* Helper methods for implementations
    /**********************************************************************
     */

    @SuppressWarnings("unchecked")
    protected Converter<Object,Object> _createConverter(Annotated annotated,
            Object converterDef)
    {
        if (converterDef == null) {
            return null;
        }
        if (converterDef instanceof Converter<?,?>) {
            return (Converter<Object,Object>) converterDef;
        }
        if (!(converterDef instanceof Class)) {
            throw new IllegalStateException("`AnnotationIntrospector` returned `Converter` definition of type "
                    +ClassUtil.classNameOf(converterDef)+"; expected type `Converter` or `Class<Converter>` instead");
        }
        Class<?> converterClass = (Class<?>)converterDef;
        // there are some known "no class" markers to consider too:
        if (converterClass == Converter.None.class || ClassUtil.isBogusClass(converterClass)) {
            return null;
        }
        if (!Converter.class.isAssignableFrom(converterClass)) {
            throw new IllegalStateException("AnnotationIntrospector returned `Class<"
                    +ClassUtil.classNameOf(converterClass)+"`>; expected `Class<Converter>`");
        }
        HandlerInstantiator hi = getHandlerInstantiator();
        Converter<?,?> conv = (hi == null) ? null : hi.converterInstance(this, annotated, converterClass);
        if (conv == null) {
            conv = (Converter<?,?>) ClassUtil.createInstance(converterClass,
                    canOverrideAccessModifiers());
        }
        return (Converter<Object,Object>) conv;
    }
}