DecorableTSFactory.java

package tools.jackson.core.base;

import java.io.*;
import java.util.ArrayList;
import java.util.List;

import tools.jackson.core.*;
import tools.jackson.core.io.*;
import tools.jackson.core.util.JsonGeneratorDecorator;

/**
 * Intermediate base {@link TokenStreamFactory} implementation that offers support for
 * streams that allow decoration of low-level input sources and output targets.
 *
 * @since 3.0
 */
public abstract class DecorableTSFactory
    extends TokenStreamFactory
    implements java.io.Serializable
{
    private static final long serialVersionUID = 3L;

    /**
     * Since factory instances are immutable, a Builder class is needed for creating
     * configurations for differently configured factory instances.
     *
     * @since 3.0
     */
    public abstract static class DecorableTSFBuilder<F extends TokenStreamFactory,
        T extends TSFBuilder<F,T>>
        extends TSFBuilder<F,T>
    {
        /**
         * Optional helper object that may decorate input sources, to do
         * additional processing on input during parsing.
         */
        protected InputDecorator _inputDecorator;

        /**
         * Optional helper object that may decorate output object, to do
         * additional processing on output during content generation.
         */
        protected OutputDecorator _outputDecorator;

        protected List<JsonGeneratorDecorator> _generatorDecorators;

        // // // Construction

        protected DecorableTSFBuilder(StreamReadConstraints src,
                StreamWriteConstraints swc, ErrorReportConfiguration erc,
                int formatPF, int formatGF) {
            super(src, swc, erc, formatPF, formatGF);
            _inputDecorator = null;
            _outputDecorator = null;
            _generatorDecorators = null;
        }

        protected DecorableTSFBuilder(DecorableTSFactory base)
        {
            super(base);
            _inputDecorator = base.getInputDecorator();
            _outputDecorator = base.getOutputDecorator();
            _generatorDecorators = base.getGeneratorDecorators();
        }

        // // // Accessors
        public InputDecorator inputDecorator() { return _inputDecorator; }
        public OutputDecorator outputDecorator() { return _outputDecorator; }
        public List<JsonGeneratorDecorator> generatorDecorators() { return _generatorDecorators; }

        // // // Decorators

        public T inputDecorator(InputDecorator dec) {
            _inputDecorator = dec;
            return _this();
        }

        public T outputDecorator(OutputDecorator dec) {
            _outputDecorator = dec;
            return _this();
        }

        public T addDecorator(JsonGeneratorDecorator dec) {
            if (_generatorDecorators == null) {
                _generatorDecorators = new ArrayList<>();
            }
            _generatorDecorators.add(dec);
            return _this();
        }
    }

    /*
    /**********************************************************************
    /* Configuration
    /**********************************************************************
     */

    /**
     * Optional helper object that may decorate input sources, to do
     * additional processing on input during parsing.
     */
    protected final InputDecorator _inputDecorator;

    /**
     * Optional helper object that may decorate output object, to do
     * additional processing on output during content generation.
     */
    protected final OutputDecorator _outputDecorator;

    protected List<JsonGeneratorDecorator> _generatorDecorators;

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

    protected DecorableTSFactory(StreamReadConstraints src, StreamWriteConstraints swc,
            ErrorReportConfiguration erc,
            int formatPF, int formatGF) {
        super(src, swc, erc, formatPF, formatGF);
        _inputDecorator = null;
        _outputDecorator = null;
        _generatorDecorators = null;
    }

    /**
     * Constructor used by builders for instantiation.
     *
     * @param baseBuilder Builder with configurations to use
     *
     * @since 3.0
     */
    protected DecorableTSFactory(DecorableTSFBuilder<?,?> baseBuilder)
    {
        super(baseBuilder);
        _inputDecorator = baseBuilder.inputDecorator();
        _outputDecorator = baseBuilder.outputDecorator();
        _generatorDecorators = _copy(baseBuilder.generatorDecorators());
    }

    // Copy constructor.
    protected DecorableTSFactory(DecorableTSFactory src) {
        super(src);
        _inputDecorator = src.getInputDecorator();
        _outputDecorator = src.getOutputDecorator();
        _generatorDecorators = _copy(src._generatorDecorators);
    }

    protected static <T> List<T> _copy(List<T> src) {
        if (src == null) {
            return src;
        }
        return new ArrayList<T>(src);
    }

    /*
    /**********************************************************************
    /* Configuration, decorators
    /**********************************************************************
     */

    public OutputDecorator getOutputDecorator() {
        return _outputDecorator;
    }

    public InputDecorator getInputDecorator() {
        return _inputDecorator;
    }

    public List<JsonGeneratorDecorator> getGeneratorDecorators() {
        return _copy(_generatorDecorators);
    }

    /*
    /**********************************************************************
    /* Decorators, input
    /**********************************************************************
     */

    protected InputStream _decorate(IOContext ioCtxt, InputStream in) throws JacksonException
    {
        if (_inputDecorator != null) {
            InputStream in2 = _inputDecorator.decorate(ioCtxt, in);
            if (in2 != null) {
                return in2;
            }
        }
        return in;
    }

    protected Reader _decorate(IOContext ioCtxt, Reader in) throws JacksonException
    {
        if (_inputDecorator != null) {
            Reader in2 = _inputDecorator.decorate(ioCtxt, in);
            if (in2 != null) {
                return in2;
            }
        }
        return in;
    }

    protected DataInput _decorate(IOContext ioCtxt, DataInput in) throws JacksonException
    {
        if (_inputDecorator != null) {
            DataInput in2 = _inputDecorator.decorate(ioCtxt, in);
            if (in2 != null) {
                return in2;
            }
        }
        return in;
    }

    /*
    /**********************************************************************
    /* Decorators, output
    /**********************************************************************
     */

    protected OutputStream _decorate(IOContext ioCtxt, OutputStream out) throws JacksonException
    {
        if (_outputDecorator != null) {
            OutputStream out2 = _outputDecorator.decorate(ioCtxt, out);
            if (out2 != null) {
                return out2;
            }
        }
        return out;
    }

    protected Writer _decorate(IOContext ioCtxt, Writer out) throws JacksonException
    {
        if (_outputDecorator != null) {
            Writer out2 = _outputDecorator.decorate(ioCtxt, out);
            if (out2 != null) {
                return out2;
            }
        }
        return out;
    }

    protected JsonGenerator _decorate(JsonGenerator result) {
        if (_generatorDecorators != null) {
            for(JsonGeneratorDecorator decorator : _generatorDecorators) {
                result = decorator.decorate(this, result);
            }
        }
        return result;
    }

    /*
    /**********************************************************************
    /* Helper methods for failed construction
    /**********************************************************************
     */

    /**
     * Method called when construction of parser or generator fails: releases
     * {@link IOContext} so that {@link tools.jackson.core.util.BufferRecycler}
     * it leased gets returned to the pool. Without this the lease is simply
     * dropped, since context is otherwise only released when parser or
     * generator that owns it gets closed.
     *
     * @param ioCtxt Context to release
     * @param failure Failure to add possible secondary failure to, as suppressed
     *
     * @since 3.1.7
     */
    static void _releaseOnFailedConstruction(IOContext ioCtxt, RuntimeException failure)
    {
        try {
            ioCtxt.close();
        } catch (Exception e) {
            failure.addSuppressed(e);
        }
    }

    /**
     * Method called when construction of parser or generator fails after
     * Jackson has opened source or target itself (from {@link java.io.File} or
     * {@link java.nio.file.Path}): closes it so that it does not get leaked.
     * Caller-provided sources and targets are never passed here.
     *
     * @param toClose Source/target Jackson opened, if any ({@code null} if not yet opened)
     * @param failure Failure to add possible secondary failure to, as suppressed
     *
     * @since 3.1.7
     */
    protected static void _closeOnFailedConstruction(Closeable toClose, RuntimeException failure)
    {
        if (toClose != null) {
            try {
                toClose.close();
            } catch (Exception e) {
                failure.addSuppressed(e);
            }
        }
    }
}