TextualTSFactory.java

package tools.jackson.core.base;

import java.io.*;
import java.nio.file.Path;

import tools.jackson.core.*;
import tools.jackson.core.io.ContentReference;
import tools.jackson.core.io.IOContext;
import tools.jackson.core.io.UTF8Writer;

/**
 * Intermediate {@link TokenStreamFactory} sub-class used as the base for
 * textual data formats.
 */
@SuppressWarnings("resource")
public abstract class TextualTSFactory
    extends DecorableTSFactory
    implements java.io.Serializable
{
    private static final long serialVersionUID = 3L;

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

    protected TextualTSFactory(StreamReadConstraints src,
            StreamWriteConstraints swc, ErrorReportConfiguration erc,
            int formatPF, int formatGF) {
        super(src, swc, erc, formatPF, formatGF);
    }

    /**
     * Constructors used by builders for instantiation.
     *
     * @param baseBuilder Builder with configurations to use
     *
     * @since 3.0
     */
    protected TextualTSFactory(DecorableTSFBuilder<?,?> baseBuilder)
    {
        super(baseBuilder);
    }

    // Copy constructor.
    protected TextualTSFactory(TextualTSFactory src) {
        super(src);
    }

    /*
    /**********************************************************************
    /* Default introspection
    /**********************************************************************
     */

    @Override
    public boolean canHandleBinaryNatively() {
        // typically textual formats need escaping like Base64 so:
        return false;
    }

    /*
    /**********************************************************************
    /* Extended capabilities for textual formats (only)
    /**********************************************************************
     */

    /**
     * Introspection method that can be used by base factory to check
     * whether access using <code>char[]</code> is something that actual
     * parser implementations can take advantage of, over having to
     * use {@link java.io.Reader}. Sub-types are expected to override
     * definition; default implementation (suitable for JSON) alleges
     * that optimization are possible; and thereby is likely to try
     * to access {@link java.lang.String} content by first copying it into
     * recyclable intermediate buffer.
     *<p>
     * Default implementation simply returns {@code true}
     *
     * @return Whether efficient access is possible
     */
    public boolean canUseCharArrays() { return true; }

    /*
    /**********************************************************************
    /* Factory methods: parsers, with context
    /**********************************************************************
     */

    @Override
    public JsonParser createParser(ObjectReadContext readCtxt, File f)
        throws JacksonException
    {
        // true, since we create InputStream from File
        IOContext ioCtxt = _createContext(_createContentReference(f), true);
        return _createParser(readCtxt, ioCtxt,
                _decorate(ioCtxt, _fileInputStream(f)));
    }

    @Override
    public JsonParser createParser(ObjectReadContext readCtxt, Path p)
        throws JacksonException
    {
        // true, since we create InputStream from Path
        IOContext ioCtxt = _createContext(_createContentReference(p), true);
        return _createParser(readCtxt, ioCtxt,
                _decorate(ioCtxt, _pathInputStream(p)));
    }

    @Override
    public JsonParser createParser(ObjectReadContext readCtxt, InputStream in)
        throws JacksonException
    {
        IOContext ioCtxt = _createContext(_createContentReference(in), false);
        return _createParser(readCtxt, ioCtxt, _decorate(ioCtxt, in));
    }

    @Override
    public JsonParser createParser(ObjectReadContext readCtxt, Reader r)
        throws JacksonException
    {
        // false -> we do NOT own Reader (did not create it)
        IOContext ioCtxt = _createContext(_createContentReference(r), false);
        return _createParser(readCtxt, ioCtxt, _decorate(ioCtxt, r));
    }

    @Override
    public JsonParser createParser(ObjectReadContext readCtxt,
            byte[] data, int offset, int len)
        throws JacksonException
    {
        IOContext ioCtxt = _createContext(_createContentReference(data, offset, len), true);
        if (_inputDecorator != null) {
            InputStream in = _inputDecorator.decorate(ioCtxt, data, offset, len);
            if (in != null) {
                return _createParser(readCtxt, ioCtxt, in);
            }
        }
        return _createParser(readCtxt, ioCtxt, data, offset, len);
    }

    @Override
    public JsonParser createParser(ObjectReadContext readCtxt, String content)
        throws JacksonException
    {
        final int strLen = content.length();
        // Actually, let's use this for medium-sized content, up to 64kB chunk (32kb char)
        if ((_inputDecorator != null) || (strLen > 0x8000) || !canUseCharArrays()) {
            // easier to just wrap in a Reader than extend InputDecorator; or, if content
            // is too long for us to copy it over
            return createParser(readCtxt, new StringReader(content));
        }
        IOContext ioCtxt = _createContext(_createContentReference(content), true);
        char[] buf = ioCtxt.allocTokenBuffer(strLen);
        content.getChars(0, strLen, buf, 0);
        return _createParser(readCtxt, ioCtxt, buf, 0, strLen, true);
    }

    @Override
    public JsonParser createParser(ObjectReadContext readCtxt,
            char[] content, int offset, int len)
        throws JacksonException
    {
        if (_inputDecorator != null) { // easier to just wrap in a Reader than extend InputDecorator
            return createParser(readCtxt, new CharArrayReader(content, offset, len));
        }
        return _createParser(readCtxt,
                _createContext(_createContentReference(content, offset, len), true),
                content, offset, len,
                // important: buffer is NOT recyclable, as it's from caller
                false);
    }

    @Override
    public JsonParser createParser(ObjectReadContext readCtxt,
            DataInput in) throws JacksonException
    {
        IOContext ioCtxt = _createContext(_createContentReference(in), false);
        return _createParser(readCtxt, ioCtxt, _decorate(ioCtxt, in));
    }

    protected abstract JsonParser _createParser(ObjectReadContext readCtxt,
            IOContext ctxt, InputStream in) throws JacksonException;

    protected abstract JsonParser _createParser(ObjectReadContext readCtxt,
            IOContext ctxt, Reader r) throws JacksonException;

    protected abstract JsonParser _createParser(ObjectReadContext readCtxt,
            IOContext ctxt,
            byte[] data, int offset, int len) throws JacksonException;

    protected abstract JsonParser _createParser(ObjectReadContext readCtxt,
            IOContext ctxt, char[] data, int offset, int len, boolean recyclable)
                    throws JacksonException;

    protected abstract JsonParser _createParser(ObjectReadContext readCtxt,
            IOContext ctxt, DataInput input) throws JacksonException;

    /*
    /**********************************************************************
    /* Factory methods: generators
    /**********************************************************************
     */

    @Override
    public JsonGenerator createGenerator(ObjectWriteContext writeCtxt,
            OutputStream out, JsonEncoding enc)
        throws JacksonException
    {
        // false -> we won't manage the stream unless explicitly directed to
        IOContext ioCtxt = _createContext(_createContentReference(out), false, enc);
        if (enc == JsonEncoding.UTF8) {
            return _decorate(
                    _createUTF8Generator(writeCtxt, ioCtxt, _decorate(ioCtxt, out))
            );
        }
        return _decorate(
                _createGenerator(writeCtxt, ioCtxt,
                        _decorate(ioCtxt, _createWriter(ioCtxt, out, enc)))
        );
    }

    @Override
    public JsonGenerator createGenerator(ObjectWriteContext writeCtxt, Writer w)
        throws JacksonException
    {
        IOContext ioCtxt = _createContext(_createContentReference(w), false);
        return _decorate(
                _createGenerator(writeCtxt, ioCtxt, _decorate(ioCtxt, w))
        );
    }

    @Override
    public JsonGenerator createGenerator(ObjectWriteContext writeCtxt,
            File f, JsonEncoding enc)
        throws JacksonException
    {
        final OutputStream out = _fileOutputStream(f);
        final IOContext ioCtxt = _createContext(_createContentReference(f), true, enc);
        if (enc == JsonEncoding.UTF8) {
            return _decorate(
                    _createUTF8Generator(writeCtxt, ioCtxt, _decorate(ioCtxt, out))
            );
        }
        return _decorate(
                _createGenerator(writeCtxt, ioCtxt,
                        _decorate(ioCtxt, _createWriter(ioCtxt, out, enc)))
        );
    }

    @Override
    public JsonGenerator createGenerator(ObjectWriteContext writeCtxt,
            Path p, JsonEncoding enc)
        throws JacksonException
    {
        final OutputStream out = _pathOutputStream(p);
        final IOContext ioCtxt = _createContext(_createContentReference(p), true, enc);
        if (enc == JsonEncoding.UTF8) {
            return _decorate(
                    _createUTF8Generator(writeCtxt, ioCtxt, _decorate(ioCtxt, out))
            );
        }
        return _decorate(
                _createGenerator(writeCtxt, ioCtxt,
                        _decorate(ioCtxt, _createWriter(ioCtxt, out, enc)))
        );
    }

    /*
    /**********************************************************************
    /* Factory methods: context objects
    /**********************************************************************
     */

    @Override
    protected ContentReference _createContentReference(Object contentRef) {
        // true -> textual
        return ContentReference.construct(true, contentRef,
                _errorReportConfiguration);
    }

    @Override
    protected ContentReference _createContentReference(Object contentRef,
            int offset, int length)
    {
        // true -> textual
        return ContentReference.construct(true, contentRef, offset, length,
                _errorReportConfiguration);
    }

    /*
    /**********************************************************************
    /* Factory methods: abstract, for sub-classes to implement
    /**********************************************************************
     */

    /**
     * Overridable factory method that actually instantiates generator for
     * given {@link Writer} and context object.
     *<p>
     * This method is specifically designed to remain
     * compatible between minor versions so that sub-classes can count
     * on it being called as expected. That is, it is part of official
     * interface from sub-class perspective, although not a public
     * method available to users of factory implementations.
     *
     * @param writeCtxt Object write context for generator to use
     * @param ioCtxt IOContext for generator to use
     * @param out Writer for generator to use
     *
     * @return Generator constructed
     *
     * @throws JacksonException If there is a problem constructing generator
     */
    protected abstract JsonGenerator _createGenerator(ObjectWriteContext writeCtxt,
            IOContext ioCtxt, Writer out) throws JacksonException;

    /**
     * Overridable factory method that actually instantiates generator for
     * given {@link OutputStream} and context object, using UTF-8 encoding.
     *<p>
     * This method is specifically designed to remain
     * compatible between minor versions so that sub-classes can count
     * on it being called as expected. That is, it is part of official
     * interface from sub-class perspective, although not a public
     * method available to users of factory implementations.
     *
     * @param writeCtxt Object write context for generator to use
     * @param ioCtxt IOContext for generator to use
     * @param out OutputStream for generator to use
     *
     * @return Generator constructed
     *
     * @throws JacksonException If there is a problem constructing generator
     */
    protected abstract JsonGenerator _createUTF8Generator(ObjectWriteContext writeCtxt,
            IOContext ioCtxt, OutputStream out) throws JacksonException;

    protected Writer _createWriter(IOContext ioCtxt, OutputStream out, JsonEncoding enc)
        throws JacksonException
    {
        // note: this should not get called any more (caller checks, dispatches)
        if (enc == JsonEncoding.UTF8) { // We have optimized writer for UTF-8
            return new UTF8Writer(ioCtxt, out);
        }
        // not optimal, but should do unless we really care about UTF-16/32 encoding speed
        try {
            return new OutputStreamWriter(out, enc.getJavaName());
        } catch (IOException e) {
            throw _wrapIOFailure(e);
        }
    }
}