CsvFactory.java

package tools.jackson.dataformat.csv;

import java.io.*;

import tools.jackson.core.*;
import tools.jackson.core.base.TextualTSFactory;
import tools.jackson.core.io.IOContext;

import tools.jackson.dataformat.csv.impl.CsvParserBootstrapper;
import tools.jackson.dataformat.csv.impl.UTF8Writer;

public class CsvFactory
    extends TextualTSFactory
    implements java.io.Serializable
{
    private static final long serialVersionUID = 1L;

    /**
     * Name used to identify CSV format.
     * (and returned by {@link #getFormatName()}
     */
    public final static String FORMAT_NAME_CSV = "CSV";
    
    /**
     * Bit field (set of flags) of all parser features that are enabled
     * by default.
     */
    protected final static int DEFAULT_CSV_PARSER_FEATURE_FLAGS = CsvReadFeature.collectDefaults();

    /**
     * Bit field (set of flags) of all generator features that are enabled
     * by default.
     */
    protected final static int DEFAULT_CSV_GENERATOR_FEATURE_FLAGS = CsvWriteFeature.collectDefaults();

    protected final static CsvSchema DEFAULT_SCHEMA = CsvSchema.emptySchema();
    
    /*
    /**********************************************************************
    /* Configuration
    /**********************************************************************
     */

    protected final CsvCharacterEscapes _characterEscapes;

    /*
    /**********************************************************************
    /* Factory construction, configuration
    /**********************************************************************
     */

    /**
     * Default constructor used to create factory instances.
     * Creation of a factory instance is a light-weight operation,
     * but it is still a good idea to reuse limited number of
     * factory instances (and quite often just a single instance):
     * factories are used as context for storing some reused
     * processing objects (such as symbol tables parsers use)
     * and this reuse only works within context of a single
     * factory instance.
     */
    public CsvFactory() {
        super(StreamReadConstraints.defaults(), StreamWriteConstraints.defaults(),
                ErrorReportConfiguration.defaults(),
                DEFAULT_CSV_PARSER_FEATURE_FLAGS,
                DEFAULT_CSV_GENERATOR_FEATURE_FLAGS);
        _characterEscapes = null; // derive from flags
    }

    protected CsvFactory(CsvFactory src)
    {
        super(src);
        _characterEscapes = src._characterEscapes;
    }

    /**
     * Constructors used by {@link CsvFactoryBuilder} for instantiation.
     *
     * @since 3.0
     */
    protected CsvFactory(CsvFactoryBuilder b)
    {
        super(b);
        _characterEscapes = b.characterEscapes();
    }

    @Override
    public CsvFactoryBuilder rebuild() {
        return new CsvFactoryBuilder(this);
    }

    /**
     * Main factory method to use for constructing {@link CsvFactory} instances with
     * different configuration.
     */
    public static CsvFactoryBuilder builder() {
        return new CsvFactoryBuilder();
    }

    @Override
    public CsvFactory copy() {
        return new CsvFactory(this);
    }

    /**
     * Instances are immutable so just return `this`
     */
    @Override
    public TokenStreamFactory snapshot() {
        return this;
    }
    
    /*
    /**********************************************************************
    /* Serializable overrides
    /**********************************************************************
     */

    /**
     * Method that we need to override to actually make restoration go
     * through constructors etc.
     * Also: must be overridden by sub-classes as well.
     */
    protected Object readResolve() {
        return new CsvFactory(this);
    }

    /*
    /**********************************************************************
    /* Capability introspection
    /**********************************************************************
     */

    @Override
    public Version version() {
        return PackageVersion.VERSION;
    }

    // Yes; CSV is positional
    @Override
    public boolean requiresPropertyOrdering() {
        return true;
    }

    // No, we can't make use of char[] optimizations
    @Override
    public boolean canUseCharArrays() { return false; }

    @Override
    public boolean canParseAsync() {
        // 30-Sep-2017, tatu: No async parsing yet
        return false;
    }

    /*
    /**********************************************************************
    /* Format support
    /**********************************************************************
     */

    @Override
    public String getFormatName() {
        return FORMAT_NAME_CSV;
    }

    @Override
    public boolean canUseSchema(FormatSchema schema) {
        return (schema instanceof CsvSchema);
    }

    @Override
    public Class<CsvReadFeature> getFormatReadFeatureType() {
        return CsvReadFeature.class;
    }

    @Override
    public Class<CsvWriteFeature> getFormatWriteFeatureType() {
        return CsvWriteFeature.class;
    }

    @Override
    public int getFormatReadFeatures() { return _formatReadFeatures; }

    @Override
    public int getFormatWriteFeatures() { return _formatWriteFeatures; }

    /**
     * Checked whether specified parser feature is enabled.
     */
    public final boolean isEnabled(CsvReadFeature f) {
        return (_formatReadFeatures & f.getMask()) != 0;
    }

    /**
     * Check whether specified generator feature is enabled.
     */
    public boolean isEnabled(CsvWriteFeature f) {
        return (_formatWriteFeatures & f.getMask()) != 0;
    }

    /*
    /**********************************************************************
    /* Factory methods: parsers
    /**********************************************************************
     */

    /**
     * Overridable factory method that actually instantiates desired parser.
     */
    @Override
    protected CsvParser _createParser(ObjectReadContext readCtxt, IOContext ioCtxt,
            InputStream in)
    {
        return new CsvParserBootstrapper(ioCtxt, in)
            .constructParser(readCtxt,
                    readCtxt.getStreamReadFeatures(_streamReadFeatures),
                    readCtxt.getFormatReadFeatures(_formatReadFeatures),
                    _getSchema(readCtxt));
    }

    @Override
    protected CsvParser _createParser(ObjectReadContext readCtxt, IOContext ioCtxt,
            byte[] data, int offset, int len)
    {
        return new CsvParserBootstrapper(ioCtxt, data, offset, len)
               .constructParser(readCtxt,
                       readCtxt.getStreamReadFeatures(_streamReadFeatures),
                       readCtxt.getFormatReadFeatures(_formatReadFeatures),
                       _getSchema(readCtxt));
    }

    /**
     * Overridable factory method that actually instantiates desired parser.
     */
    @Override
    protected CsvParser _createParser(ObjectReadContext readCtxt, IOContext ioCtxt,
            Reader r)
    {
        return new CsvParser(readCtxt, ioCtxt,
                readCtxt.getStreamReadFeatures(_streamReadFeatures),
                readCtxt.getFormatReadFeatures(_formatReadFeatures),
                _getSchema(readCtxt),
                r);
    }

    @Override
    protected CsvParser _createParser(ObjectReadContext readCtxt, IOContext ioCtxt,
            char[] data, int offset, int len,
            boolean recyclable)
    {
        return new CsvParser(readCtxt, ioCtxt,
                readCtxt.getStreamReadFeatures(_streamReadFeatures),
                readCtxt.getFormatReadFeatures(_formatReadFeatures),
                _getSchema(readCtxt),
                new CharArrayReader(data, offset, len));
    }

    @Override
    protected JsonParser _createParser(ObjectReadContext readCtxt, IOContext ioCtxt,
            DataInput input)
    {
        return _unsupported();
    }

    private final CsvSchema _getSchema(ObjectReadContext readCtxt) {
        FormatSchema sch = readCtxt.getSchema();
        if (sch == null) {
            return DEFAULT_SCHEMA;
        }
        return (CsvSchema) sch;
    }
    
    /*
    /**********************************************************************
    /* Factory methods: generators
    /**********************************************************************
     */
    
    @Override
    protected CsvGenerator _createGenerator(ObjectWriteContext writeCtxt,
            IOContext ioCtxt, Writer out)
    {
        return new CsvGenerator(writeCtxt, ioCtxt,
                writeCtxt.getStreamWriteFeatures(_streamWriteFeatures),
                writeCtxt.getFormatWriteFeatures(_formatWriteFeatures),
                out, _getSchema(writeCtxt), _characterEscapes);
    }

    @SuppressWarnings("resource")
    @Override
    protected CsvGenerator _createUTF8Generator(ObjectWriteContext writeCtxt,
            IOContext ioCtxt, OutputStream out)
    {
        return new CsvGenerator(writeCtxt, ioCtxt,
                writeCtxt.getStreamWriteFeatures(_streamWriteFeatures),
                writeCtxt.getFormatWriteFeatures(_formatWriteFeatures),
                new UTF8Writer(ioCtxt, out), _getSchema(writeCtxt),
                _characterEscapes);
    }

    private final CsvSchema _getSchema(ObjectWriteContext writeCtxt) {
        FormatSchema sch = writeCtxt.getSchema();
        if (sch == null) {
            return DEFAULT_SCHEMA;
        }
        return (CsvSchema) sch;
    }
}