MapperConfiguratorBase.java

package org.glassfish.jersey.jackson3.internal.jackson.jakarta.rs.cfg;

import java.util.EnumMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;

import tools.jackson.databind.AnnotationIntrospector;
import tools.jackson.databind.DeserializationFeature;
import tools.jackson.databind.MapperFeature;
import tools.jackson.databind.ObjectMapper;
import tools.jackson.databind.SerializationFeature;
import tools.jackson.databind.cfg.MapperBuilder;

/**
 * Helper class used to encapsulate details of configuring an
 * {@link ObjectMapper} instance to be used for data binding, as
 * well as accessing it.
 */
public abstract class MapperConfiguratorBase<IMPL extends MapperConfiguratorBase<IMPL,MAPPER>,
        MAPPER extends ObjectMapper
>
{
    private final ReentrantLock _lock = new ReentrantLock();

    /*
    /**********************************************************************
    /* Configuration, simple features
    /**********************************************************************
     */

    /**
     * {@link DeserializationFeature}s to explicitly enable or disable
     */
    protected EnumMap<MapperFeature, Boolean> _mapperFeatures;

    /**
     * {@link DeserializationFeature}s to explicitly enable or disable
     */
    protected EnumMap<DeserializationFeature, Boolean> _deserFeatures;

    /**
     * {@link SerializationFeature}s to explicitly enable or disable
     */
    protected EnumMap<SerializationFeature, Boolean> _serFeatures;

    /*
    /**********************************************************************
    /* Configuration, other
    /**********************************************************************
     */

    /**
     * {@code AnnotationIntrospector} to use as an override over default
     * {@code JacksonAnnotationIntrospector}, if any.
     *
     * @since 3.0
     */
    protected AnnotationIntrospector _instropectorOverride;

    /*
    /**********************************************************************
    /* Lazily constructed Mapper instance(s)
    /**********************************************************************
     */

    /**
     * Mapper provider was constructed with if any, or that was constructed
     * due to a call to explicitly configure mapper.
     * If defined (explicitly or implicitly) it will be used, instead
     * of using provider-based lookup.
     */
    protected MAPPER _mapper;

    /**
     * If no mapper was specified when constructed, and no configuration
     * calls are made, a default mapper is constructed. The difference
     * between default mapper and regular one is that default mapper
     * is only used if no mapper is found via provider lookup.
     */
    protected MAPPER _defaultMapper;

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

    public MapperConfiguratorBase(MAPPER mapper,
                                  AnnotationIntrospector instropectorOverride)
    {
        _mapper = mapper;
        _instropectorOverride = instropectorOverride;
    }

    public MAPPER getDefaultMapper() {
        if (_defaultMapper == null) {
            _lock.lock();
            try {
                if (_defaultMapper == null) {
                    _defaultMapper = _mapperWithConfiguration(mapperBuilder());
                }
            } finally {
                _lock.unlock();
            }        }
        return _defaultMapper;
    }

    /**
     * Helper method that will ensure that there is a configurable non-default
     * mapper (constructing an instance if one didn't yet exit), and return
     * that mapper.
     */
    protected MAPPER mapper()
    {
        if (_mapper == null) {
            _lock.lock();
            try {
                if (_mapper == null) {
                    _mapper = _mapperWithConfiguration(mapperBuilder());
                }
            } finally {
                _lock.unlock();
            }
        }
        return _mapper;
    }

    /*
    /**********************************************************************
    /* Abstract methods to implement
    /**********************************************************************
     */

    /**
     * @since 3.0
     */
    protected abstract MapperBuilder<?,?> mapperBuilder();

    /*
    /**********************************************************************
    /* Configuration methods
    /**********************************************************************
     */

    /**
     * Method that locates, configures and returns {@link ObjectMapper} to use
     */
    public MAPPER getConfiguredMapper() {
        // important: should NOT call mapper(); needs to return null
        // if no instance has been passed or constructed
        return _mapper;
    }

    public final void setMapper(MAPPER m) {
        _mapper = m;
    }

    public final void setAnnotationIntrospector(AnnotationIntrospector aiOverride) {
        _instropectorOverride = aiOverride;
    }

    public final void configure(DeserializationFeature f, boolean state) {
        if (_deserFeatures == null) {
            _deserFeatures = new EnumMap<>(DeserializationFeature.class);
        }
        _deserFeatures.put(f, state);
    }

    public final void configure(SerializationFeature f, boolean state) {
        if (_serFeatures == null) {
            _serFeatures = new EnumMap<>(SerializationFeature.class);
        }
        _serFeatures.put(f, state);
    }

    /*
    /**********************************************************************
    /* Helper methods for sub-classes
    /**********************************************************************
     */

    /**
     * Helper method that will configure given builder using configured overrides.
     */
    @SuppressWarnings("unchecked")
    protected MAPPER _mapperWithConfiguration(MapperBuilder<?,?> mapperBuilder)
    {
        return (MAPPER) _builderWithConfiguration(mapperBuilder)
                .build();
    }

    /**
     * Overridable helper method that applies all configuration on given builder.
     */
    protected MapperBuilder<?,?> _builderWithConfiguration(MapperBuilder<?,?> mapperBuilder)
    {
        // First, AnnotationIntrospector settings
        if (_instropectorOverride != null) {
            mapperBuilder = mapperBuilder.annotationIntrospector(_instropectorOverride);
        }

        // Features?
        if (_mapperFeatures != null) {
            for (Map.Entry<MapperFeature,Boolean> entry : _mapperFeatures.entrySet()) {
                mapperBuilder = mapperBuilder.configure(entry.getKey(), entry.getValue());
            }
        }
        if (_serFeatures != null) {
            for (Map.Entry<SerializationFeature,Boolean> entry : _serFeatures.entrySet()) {
                mapperBuilder = mapperBuilder.configure(entry.getKey(), entry.getValue());
            }
        }
        if (_deserFeatures != null) {
            for (Map.Entry<DeserializationFeature,Boolean> entry : _deserFeatures.entrySet()) {
                mapperBuilder = mapperBuilder.configure(entry.getKey(), entry.getValue());
            }
        }

        // anything else?
        return mapperBuilder;
    }
}