TokenStreamFactory.java

/* Jackson JSON-processor.
 *
 * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi
 */
package tools.jackson.core;

import java.io.*;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Locale;
import java.util.Objects;

import tools.jackson.core.async.ByteArrayFeeder;
import tools.jackson.core.async.ByteBufferFeeder;
import tools.jackson.core.exc.JacksonIOException;
import tools.jackson.core.exc.StreamReadException;
import tools.jackson.core.io.*;
import tools.jackson.core.json.JsonFactory;
import tools.jackson.core.sym.PropertyNameMatcher;
import tools.jackson.core.sym.SimpleNameMatcher;
import tools.jackson.core.util.BufferRecycler;
import tools.jackson.core.util.JacksonFeature;
import tools.jackson.core.util.JsonRecyclerPools;
import tools.jackson.core.util.Named;
import tools.jackson.core.util.RecyclerPool;
import tools.jackson.core.util.Snapshottable;

/**
 * The main factory class of Jackson streaming package, used to configure and
 * construct token reader (aka parser, {@link JsonParser})
 * and token writer (aka generator, {@link JsonGenerator})
 * instances.
 *<p>
 * Factory instances are thread-safe and reusable after configuration
 * (if any). Typically applications and services use only a single
 * globally shared factory instance, unless they need differently
 * configured factories. Factory reuse is important if efficiency matters;
 * most recycling of expensive construct is done on per-factory basis.
 *<p>
 * Creation of a factory instance is a light-weight operation.
 *
 * @since 3.0 (refactored out of {@link JsonFactory})
 */
public abstract class TokenStreamFactory
    implements Versioned,
        java.io.Serializable,
        Snapshottable<TokenStreamFactory> // 3.0
{
    private static final long serialVersionUID = 3L;

    /**
     * Shareable stateles "empty" {@link ObjectWriteContext} Object, passed in
     * cases where caller had not passed actual context - "null object" of sort.
     */
    public final static ObjectWriteContext EMPTY_WRITE_CONTEXT = new ObjectWriteContext.Base();

    /*
    /**********************************************************************
    /* Helper types
    /**********************************************************************
     */

    /**
     * Enumeration that defines all on/off features that can only be
     * changed for {@link TokenStreamFactory}.
     */
    public enum Feature
        implements JacksonFeature
    {

        // // // Symbol handling (interning etc)

        /**
         * Feature that determines whether JSON object property names are
         * to be canonicalized using {@link String#intern} or not:
         * if enabled, all property names will be intern()ed (and caller
         * can count on this being true for all such names); if disabled,
         * no intern()ing is done. There may still be basic
         * canonicalization (that is, same String will be used to represent
         * all identical object property names for a single document).
         *<p>
         * Note: this setting only has effect if
         * {@link #CANONICALIZE_PROPERTY_NAMES} is true -- otherwise no
         * canonicalization of any sort is done.
         *<p>
         * This setting is disabled by default since 3.0 (was enabled in 1.x and 2.x)
         */
        INTERN_PROPERTY_NAMES(false),

        /**
         * Feature that determines whether JSON object property names are
         * to be canonicalized (details of how canonicalization is done
         * then further specified by
         * {@link #INTERN_PROPERTY_NAMES}).
         *<p>
         * This setting is enabled by default.
         */
        CANONICALIZE_PROPERTY_NAMES(true),

        /**
         * Feature that determines what happens if we encounter a case in symbol
         * handling where number of hash collisions exceeds a safety threshold
         * -- which almost certainly means a denial-of-service attack via generated
         * duplicate hash codes.
         * If feature is enabled, an {@link IllegalStateException} is
         * thrown to indicate the suspected denial-of-service attack; if disabled, processing continues but
         * canonicalization (and thereby <code>intern()</code>ing) is disabled) as protective
         * measure.
         *<p>
         * This setting is enabled by default.
         */
        FAIL_ON_SYMBOL_HASH_OVERFLOW(true),

        /**
         * Feature to control charset detection for byte-based inputs ({@code byte[]}, {@link InputStream}...). When
         * this feature is enabled (the default), the factory will allow UTF-16 and UTF-32 inputs and try to detect
         * them, as specified by RFC 4627. When this feature is disabled the factory will assume UTF-8, as specified
         * by RFC 8259.
         *<p>
         * NOTE: only applies to some implementations: most notably for {@code JsonFactory}.
         */
        CHARSET_DETECTION(true),        
        ;

        /**
         * Whether feature is enabled or disabled by default.
         */
        private final boolean _defaultState;

        /**
         * Method that calculates bit set (flags) of all features that
         * are enabled by default.
         *
         * @return Bit mask of all features that are enabled by default
         */
        public static int collectDefaults() {
            int flags = 0;
            for (Feature f : values()) {
                if (f.enabledByDefault()) { flags |= f.getMask(); }
            }
            return flags;
        }

        private Feature(boolean defaultState) { _defaultState = defaultState; }

        @Override
        public boolean enabledByDefault() { return _defaultState; }
        @Override
        public boolean enabledIn(int flags) { return (flags & getMask()) != 0; }
        @Override
        public int getMask() { return (1 << ordinal()); }
    }

    /*
    /**********************************************************************
    /* Constants
    /**********************************************************************
     */

    /**
     * Bitfield (set of flags) of all factory features that are enabled by default.
     */
    protected final static int DEFAULT_FACTORY_FEATURE_FLAGS = TokenStreamFactory.Feature.collectDefaults();

    /**
     * Bitfield (set of flags) of all parser features that are enabled
     * by default.
     */
    protected final static int DEFAULT_STREAM_READ_FEATURE_FLAGS = StreamReadFeature.collectDefaults();

    /**
     * Bitfield (set of flags) of all generator features that are enabled
     * by default.
     */
    protected final static int DEFAULT_STREAM_WRITE_FEATURE_FLAGS = StreamWriteFeature.collectDefaults();

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

    /**
     * Currently enabled {@link TokenStreamFactory.Feature}s features as a bitmask.
     */
    protected final int _factoryFeatures;

    /**
     * Currently enabled {@link StreamReadFeature}s as a bitmask.
     */
    protected final int _streamReadFeatures;

    /**
     * Currently enabled {@link StreamWriteFeature}s as a bitmask.
     */
    protected final int _streamWriteFeatures;

    /**
     * Set of format-specific read features enabled, as bitmask.
     */
    protected final int _formatReadFeatures;

    /**
     * Set of format-specific write features enabled, as bitmask.
     */
    protected final int _formatWriteFeatures;

    /*
    /**********************************************************************
    /* Configuration, providers
    /**********************************************************************
     */

    protected final RecyclerPool<BufferRecycler> _recyclerPool;

    /*
    /**********************************************************************
    /* Configuration, constraints
    /**********************************************************************
     */
    
    /**
     * Active StreamReadConstraints to use.
     */
    protected final StreamReadConstraints _streamReadConstraints;

    /**
     * Active StreamWriteConstraints to use.
     */
    protected final StreamWriteConstraints _streamWriteConstraints;

    /**
     * Active ErrorReportConfiguration to use.
     */
    protected final ErrorReportConfiguration _errorReportConfiguration;    

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

    /**
     * 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.
     *
     * @param src StreamReadConstraints to use with parsers factory creates
     * @param swc StreamWriteConstraints to use with generators factory creates
     * @param erc ErrorReportConfiguration to use with parsers factory creates
     * @param formatReadFeatures Bitmask of format-specific read features enabled
     * @param formatWriteFeatures Bitmask of format-specific write features enabled
     */
    protected TokenStreamFactory(StreamReadConstraints src, StreamWriteConstraints swc,
            ErrorReportConfiguration erc,
            int formatReadFeatures, int formatWriteFeatures) {
        _streamReadConstraints = Objects.requireNonNull(src);
        _streamWriteConstraints = Objects.requireNonNull(swc);
        _errorReportConfiguration = Objects.requireNonNull(erc);
        _recyclerPool = JsonRecyclerPools.defaultPool();
        _factoryFeatures = DEFAULT_FACTORY_FEATURE_FLAGS;
        _streamReadFeatures = DEFAULT_STREAM_READ_FEATURE_FLAGS;
        _streamWriteFeatures = DEFAULT_STREAM_WRITE_FEATURE_FLAGS;
        _formatReadFeatures = formatReadFeatures;
        _formatWriteFeatures = formatWriteFeatures;
    }

    /**
     * Constructors used by {@link TSFBuilder} for instantiation. Base builder is
     * passed as-is to try to make interface between base types and implementations
     * less likely to change (given that sub-classing is a fragile way to do it):
     * if and when new general-purpose properties are added, implementation classes
     * do not have to use different constructors.
     *
     * @param baseBuilder Builder with configuration to use
     *
     * @since 3.0
     */
    protected TokenStreamFactory(TSFBuilder<?,?> baseBuilder)
    {
        _streamReadConstraints = Objects.requireNonNull(baseBuilder._streamReadConstraints);
        _streamWriteConstraints = Objects.requireNonNull(baseBuilder._streamWriteConstraints);
        _errorReportConfiguration = Objects.requireNonNull(baseBuilder._errorReportConfiguration);

        // 19-Apr-2024, tatu: [core#1269] allow `null` to indicate "just use
        //    default to lazily instantiate
        RecyclerPool<BufferRecycler> rp = baseBuilder._recyclerPool;
        if (rp == null) {
            rp = JsonRecyclerPools.defaultPool();
        }
        _recyclerPool = rp;

        _factoryFeatures = baseBuilder.factoryFeaturesMask();
        _streamReadFeatures = baseBuilder.streamReadFeaturesMask();
        _streamWriteFeatures = baseBuilder.streamWriteFeaturesMask();
        _formatReadFeatures = baseBuilder.formatReadFeaturesMask();
        _formatWriteFeatures = baseBuilder.formatWriteFeaturesMask();
    }

    /**
     * Constructor used if a snapshot is created, or possibly for sub-classing or
     * wrapping (delegating)
     *
     * @param src Source factory with configuration to copy
     */
    protected TokenStreamFactory(TokenStreamFactory src)
    {
        _streamReadConstraints = src._streamReadConstraints;
        _streamWriteConstraints = src._streamWriteConstraints;
        _errorReportConfiguration = src._errorReportConfiguration;
        _recyclerPool = src._recyclerPool;

        _factoryFeatures = src._factoryFeatures;
        _streamReadFeatures = src._streamReadFeatures;
        _streamWriteFeatures = src._streamWriteFeatures;
        _formatReadFeatures = src._formatReadFeatures;
        _formatWriteFeatures = src._formatWriteFeatures;
    }

    /**
     * Method similar to {@link #snapshot()}, but one that forces creation of actual
     * new copy that does NOT share any state, even non-visible to calling code,
     * such as symbol table reuse.
     *<p>
     * Implementation should be functionally equivalent to:
     *<pre>
     *    factoryInstance.rebuild().build();
     *</pre>
     *
     * @return Newly constructed stream factory instance of same type as this one,
     *    with identical configuration settings
     */
    public abstract TokenStreamFactory copy();

    /**
     * Method for constructing a new {@link TokenStreamFactory} that has
     * the same settings as this instance, but is otherwise
     * independent (i.e. nothing is actually shared, symbol tables
     * are separate).
     *<p>
     * Although assumption is that all factories are immutable -- and that we could
     * usually simply return `this` -- method is left unimplemented at this level
     * to make implementors aware of requirement to consider immutability.
     *
     * @return This factory instance to allow call chaining
     *
     * @since 3.0
     */
    @Override
    public abstract TokenStreamFactory snapshot();

    /**
     * Method that can be used to create differently configured stream factories: it will
     * create and return a Builder instance with exact settings of this stream factory.
     *
     * @return Builder instance initialized with configuration this stream factory has
     *
     * @since 3.0
     */
    public abstract TSFBuilder<?,?> rebuild();
//    public abstract <F extends TokenStreamFactory, T extends TSFBuilder<F,T>> TSFBuilder<F,T> rebuild();

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

    /**
     * Introspection method that higher-level functionality may call
     * to see whether underlying data format requires a stable ordering
     * of object properties or not.
     * This is usually used for determining
     * whether to force a stable ordering (like alphabetic ordering by name)
     * if no ordering if explicitly specified.
     *
     * @return Whether format supported by this factory
     *   requires Object properties to be ordered.
     */
    public boolean requiresPropertyOrdering() { return false; }

    /**
     * Introspection method that higher-level functionality may call
     * to see whether underlying data format can read and write binary
     * data natively; that is, embedded it as-is without using encodings
     * such as Base64.
     *
     * @return Whether format supported by this factory
     *    supports native binary content
     */
    public abstract boolean canHandleBinaryNatively();

    /**
     * Introspection method that can be used to check whether this
     * factory can create non-blocking parsers: parsers that do not
     * use blocking I/O abstractions but instead use a
     * {@link tools.jackson.core.async.NonBlockingInputFeeder}.
     *
     * @return Whether this factory supports non-blocking ("async") parsing or
     *    not (and consequently whether {@code createNonBlockingXxx()} method(s) work)
     */
    public abstract boolean canParseAsync();

    /**
     * Method for accessing kind of {@link FormatFeature} that a parser
     * {@link JsonParser} produced by this factory would accept, if any;
     * <code>null</code> returned if none.
     *
     * @return Type of format-specific stream read features, if any; {@code null} if none
     */
    public Class<? extends FormatFeature> getFormatReadFeatureType() {
        return null;
    }

    /**
     * Method for accessing kind of {@link FormatFeature} that a parser
     * {@link JsonGenerator} produced by this factory would accept, if any;
     * <code>null</code> returned if none.
     *
     * @return Type of format-specific stream read features, if any; {@code null} if none
     */
    public Class<? extends FormatFeature> getFormatWriteFeatureType() {
        return null;
    }

    /*
    /**********************************************************************
    /* Format detection functionality
    /**********************************************************************
     */

    /**
     * Method that can be used to quickly check whether given schema
     * is something that parsers and/or generators constructed by this
     * factory could use. Note that this means possible use, at the level
     * of data format (i.e. schema is for same data format as parsers and
     * generators this factory constructs); individual schema instances
     * may have further usage restrictions.
     *
     * @param schema Schema instance to check
     *
     * @return Whether parsers and generators constructed by this factory
     *   can use specified format schema instance
     */
    public abstract boolean canUseSchema(FormatSchema schema);

    /**
     * Method that returns short textual id identifying format
     * this factory supports.
     *
     * @return Name of the format handled by parsers, generators this factory creates
     */
    public abstract String getFormatName();

    /*
    /**********************************************************************
    /* Versioned
    /**********************************************************************
     */

    @Override
    public abstract Version version();

    /*
    /**********************************************************************
    /* Configuration, access to features
    /**********************************************************************
     */

    /**
     * Checked whether specified parser feature is enabled.
     *
     * @param f Feature to check
     *
     * @return {@code True} if feature is enabled; {@code false} otherwise
     */
    public final boolean isEnabled(TokenStreamFactory.Feature f) {
        return (_factoryFeatures & f.getMask()) != 0;
    }

    /**
     * Checked whether specified parser feature is enabled.
     *
     * @param f Feature to check
     *
     * @return {@code True} if feature is enabled; {@code false} otherwise
     */
    public final boolean isEnabled(StreamReadFeature f) {
        return (_streamReadFeatures & f.getMask()) != 0;
    }

    /**
     * Checked whether specified parser feature is enabled.
     *
     * @param f Feature to check
     *
     * @return {@code True} if feature is enabled; {@code false} otherwise
     */
    public final boolean isEnabled(StreamWriteFeature f) {
        return (_streamWriteFeatures & f.getMask()) != 0;
    }

    public final int getFactoryFeatures() {
        return _factoryFeatures;
    }

    // @since 3.0
    public final int getStreamReadFeatures() {
        return _streamReadFeatures;
    }

    // @since 3.0
    public final int getStreamWriteFeatures() {
        return _streamWriteFeatures;
    }

    // @since 3.0
    public int getFormatReadFeatures() { return _formatReadFeatures; }

    // @since 3.0
    public int getFormatWriteFeatures() { return _formatWriteFeatures; }

    public StreamReadConstraints streamReadConstraints() { return _streamReadConstraints; }

    public StreamWriteConstraints streamWriteConstraints() { return _streamWriteConstraints; }

    public ErrorReportConfiguration errorReportConfiguration() { return _errorReportConfiguration; }

    /*
    /**********************************************************************
    /* Factory methods for helper objects
    /**********************************************************************
     */

    /**
     * Factory method for constructing case-sensitive {@link PropertyNameMatcher}
     * for given names. It will call {@link String#intern} on names unless specified
     * that this has already been done by caller.
     *
     * @param matches Names to match, including both primary names and possible aliases
     * @param alreadyInterned Whether name Strings are already {@code String.intern()ed} or not
     *
     * @return Case-sensitive {@link PropertyNameMatcher} instance to use
     */
    public PropertyNameMatcher constructNameMatcher(List<Named> matches, boolean alreadyInterned) {
        // 15-Nov-2017, tatu: Base implementation that is likely to work fine for
        //    most if not all implementations as it is more difficult to optimize
        return SimpleNameMatcher.constructFrom(null, matches, alreadyInterned);
    }

    /**
     * Factory method for constructing case-insensitive {@link PropertyNameMatcher}
     * for given names. It will call {@link String#intern} on names unless specified
     * that this has already been done by caller.
     *
     * @param matches Names to match, including both primary names and possible aliases
     * @param alreadyInterned Whether name Strings are already {@code String.intern()ed} or not
     * @param locale Locale to use for case-handling
     *
     * @return Case-insensitive {@link PropertyNameMatcher} instance to use
     */
    public PropertyNameMatcher constructCINameMatcher(List<Named> matches, boolean alreadyInterned,
            Locale locale) {
        return SimpleNameMatcher.constructCaseInsensitive(locale, matches, alreadyInterned);
    }

    /*
    /**********************************************************************
    /* Parser factories, traditional (blocking) I/O sources
    /**********************************************************************
     */

    /**
     * Method for constructing parser instance to decode
     * contents of specified file.
     *<p>
     * Encoding is auto-detected from contents according to JSON
     * specification recommended mechanism. Json specification
     * supports only UTF-8, UTF-16 and UTF-32 as valid encodings,
     * so auto-detection implemented only for this charsets.
     * For other charsets use {@link #createParser(java.io.Reader)}.
     *<p>
     * Underlying input stream (needed for reading contents)
     * will be <b>owned</b> (and managed, i.e. closed as need be) by
     * the parser, since caller has no access to it.
     *
     * @param readCtxt Object read context to use
     * @param f File that contains content to parse
     *
     * @return Parser constructed
     *
     * @throws JacksonException If parser construction or initialization fails
     */
    public abstract JsonParser createParser(ObjectReadContext readCtxt,
            File f) throws JacksonException;

    /**
     * Method for constructing parser instance to decode
     * contents of specified path.
     *<p>
     * Encoding is auto-detected from contents according to JSON
     * specification recommended mechanism. Json specification
     * supports only UTF-8, UTF-16 and UTF-32 as valid encodings,
     * so auto-detection implemented only for this charsets.
     * For other charsets use {@link #createParser(java.io.Reader)}.
     *<p>
     * Underlying input stream (needed for reading contents)
     * will be <b>owned</b> (and managed, i.e. closed as need be) by
     * the parser, since caller has no access to it.
     *
     * @param readCtxt Object read context to use
     * @param p Path that contains content to parse
     *
     * @return Parser constructed
     *
     * @throws JacksonException If parser construction or initialization fails
     *
     * @since 3.0
     */
    public abstract JsonParser createParser(ObjectReadContext readCtxt,
            Path p) throws JacksonException;

    /**
     * Method for constructing JSON parser instance to parse
     * the contents accessed via specified input stream.
     *<p>
     * The input stream will <b>not be owned</b> by
     * the parser, it will still be managed (i.e. closed if
     * end-of-stream is reacher, or parser close method called)
     * if (and only if) {@link tools.jackson.core.StreamReadFeature#AUTO_CLOSE_SOURCE}
     * is enabled.
     *<p>
     * Note: no encoding argument is taken since it can always be
     * auto-detected as suggested by JSON RFC. Json specification
     * supports only UTF-8, UTF-16 and UTF-32 as valid encodings,
     * so auto-detection implemented only for this charsets.
     * For other charsets use {@link #createParser(java.io.Reader)}.
     *
     * @param readCtxt Object read context to use
     * @param in InputStream to use for reading content to parse
     *
     * @return Parser constructed
     *
     * @throws JacksonException If parser construction or initialization fails
     */
    public abstract JsonParser createParser(ObjectReadContext readCtxt,
            InputStream in) throws JacksonException;

    /**
     * Method for constructing parser for parsing
     * the contents accessed via specified Reader.
     <p>
     * The read stream will <b>not be owned</b> by
     * the parser, it will still be managed (i.e. closed if
     * end-of-stream is reacher, or parser close method called)
     * if (and only if) {@link tools.jackson.core.StreamReadFeature#AUTO_CLOSE_SOURCE}
     * is enabled.
     *
     * @param readCtxt Object read context to use
     * @param r Reader to use for reading content to parse
     *
     * @return Parser constructed
     *
     * @throws JacksonException If parser construction or initialization fails
     */
    public abstract JsonParser createParser(ObjectReadContext readCtxt,
            Reader r) throws JacksonException;

    /**
     * Method for constructing parser for parsing
     * the contents of given byte array.
     *
     * @param readCtxt Object read context to use
     * @param data Content to read
     *
     * @return Parser constructed
     *
     * @throws JacksonException If parser construction or initialization fails
     */
    public JsonParser createParser(ObjectReadContext readCtxt, byte[] data) throws JacksonException {
        return createParser(readCtxt, data, 0, data.length);
    }

    /**
     * Method for constructing parser for parsing
     * the contents of given byte array.
     *
     * @param readCtxt Object read context to use
     * @param content Buffer that contains data to parse
     * @param offset Offset of the first data byte within buffer
     * @param len Length of contents to parse within buffer
     *
     * @return Parser constructed
     *
     * @throws JacksonException If parser construction or initialization fails
     */
    public abstract JsonParser createParser(ObjectReadContext readCtxt,
            byte[] content, int offset, int len) throws JacksonException;

    /**
     * Method for constructing parser for parsing contents of given String.
     *
     * @param readCtxt Object read context to use
     * @param content Content to read
     *
     * @return Parser constructed
     *
     * @throws JacksonException If parser construction or initialization fails
     */
    public abstract JsonParser createParser(ObjectReadContext readCtxt,
            String content) throws JacksonException;

    /**
     * Method for constructing parser for parsing contents of given char array.
     *
     * @param readCtxt Object read context to use
     * @param content Content to read
     *
     * @return Parser constructed
     *
     * @throws JacksonException If parser construction or initialization fails
     */
    public JsonParser createParser(ObjectReadContext readCtxt,
            char[] content) throws JacksonException {
        return createParser(readCtxt, content, 0, content.length);
    }

    /**
     * Method for constructing parser for parsing contents of given char array.
     *
     * @param readCtxt Object read context to use
     * @param content Buffer that contains data to parse
     * @param offset Offset of the first data byte within buffer
     * @param len Length of contents to parse within buffer
     *
     * @return Parser constructed
     *
     * @throws JacksonException If parser construction or initialization fails
     */
    public abstract JsonParser createParser(ObjectReadContext readCtxt,
            char[] content, int offset, int len) throws JacksonException;

    /*
    /**********************************************************************
    /* Parser factories, convenience methods that do not specify
    /* `ObjectReadContext`
    /**********************************************************************
     */

    /**
     * @param f File that contains content to parse
     * @return Parser constructed
     * @throws JacksonException If parser construction or initialization fails
     *
     * @deprecated Since 3.0 use {@link #createParser(ObjectReadContext,java.io.File)}
     */
    @Deprecated
    public JsonParser createParser(File f) throws JacksonException {
        return createParser(ObjectReadContext.empty(), f);
    }

    /**
     * @param in InputStream to use for reading content to parse
     * @return Parser constructed
     *
     * @throws JacksonException If parser construction or initialization fails
     *
     * @deprecated Since 3.0 use {@link #createParser(ObjectReadContext,java.io.InputStream)}
     */
    @Deprecated
    public JsonParser createParser(InputStream in) throws JacksonException {
        return createParser(ObjectReadContext.empty(), in);
    }

    /**
     * @param r Reader to use for reading content to parse
     * @return Parser constructed
     *
     * @throws JacksonException If parser construction or initialization fails
     *
     * @deprecated Since 3.0 use {@link #createParser(ObjectReadContext,java.io.Reader)}
     */
    @Deprecated
    public JsonParser createParser(Reader r) throws JacksonException {
        return createParser(ObjectReadContext.empty(), r);
    }

    /**
     * @param content Buffer that contains content to parse
     * @return Parser constructed
     *
     * @throws JacksonException If parser construction or initialization fails
     *
     * @deprecated Since 3.0 use {@link #createParser(ObjectReadContext,byte[])}
     */
    @Deprecated
    public JsonParser createParser(byte[] content) throws JacksonException {
        return createParser(ObjectReadContext.empty(), content, 0, content.length);
    }

    /**
     * @param content Buffer that contains content to parse
     * @param offset Offset of the first data byte within buffer
     * @param len Length of contents to parse within buffer
     *
     * @return Parser constructed
     * @throws JacksonException If parser construction or initialization fails
     *
     * @deprecated Since 3.0 use {@link #createParser(ObjectReadContext,byte[],int,int)}
     */
    @Deprecated
    public JsonParser createParser(byte[] content, int offset, int len) throws JacksonException {
        return createParser(ObjectReadContext.empty(), content, offset, len);
    }

    /**
     * @param content Content to parse
     * @return Parser constructed
     *
     * @throws JacksonException If parser construction or initialization fails
     *
     * @deprecated Since 3.0 use {@link #createParser(ObjectReadContext,String)}
     */
    @Deprecated
    public JsonParser createParser(String content) throws JacksonException {
        return createParser(ObjectReadContext.empty(), content);
    }

    /**
     * @param content Buffer that contains data to parse
     * @return Parser constructed
     *
     * @throws JacksonException If parser construction or initialization fails
     *
     * @deprecated Since 3.0 use {@link #createParser(ObjectReadContext,char[])}
     */
    @Deprecated
    public JsonParser createParser(char[] content) throws JacksonException {
        return createParser(ObjectReadContext.empty(), content, 0, content.length);
    }

    /**
     * @param content Buffer that contains data to parse
     * @param offset Offset of the first data byte within buffer
     * @param len Length of contents to parse within buffer
     *
     * @return Parser constructed
     *
     * @throws JacksonException If parser construction or initialization fails
     *
     * @deprecated Since 3.0 use {@link #createParser(ObjectReadContext,char[],int,int)}
     */
    @Deprecated
    public JsonParser createParser(char[] content, int offset, int len) throws JacksonException {
        return createParser(ObjectReadContext.empty(), content, offset, len);
    }

    /*
    /**********************************************************************
    /* Parser factories, non-stream sources
    /**********************************************************************
     */

    /**
     * Optional method for constructing parser for reading contents from specified {@link DataInput}
     * instance.
     *<p>
     * If this factory does not support {@link DataInput} as source,
     * will throw {@link UnsupportedOperationException}
     *
     * @param readCtxt Object read context to use
     * @param in InputStream to use for reading content to parse
     *
     * @return Parser constructed
     *
     * @throws JacksonException If parser construction or initialization fails
     */
    public abstract JsonParser createParser(ObjectReadContext readCtxt, DataInput in)
        throws JacksonException;

    /**
     * Optional method for constructing parser for non-blocking parsing
     * via {@link ByteArrayFeeder} interface (accessed using
     * {@link JsonParser#nonBlockingInputFeeder()} from constructed instance).
     *<p>
     * If this factory does not support non-blocking parsing (either at all,
     * or from byte array),
     * will throw {@link UnsupportedOperationException}.
     *<p>
     * Note that JSON-backed factory only supports parsing of UTF-8 encoded JSON content
     * (and US-ASCII since it is proper subset); other encodings are not supported
     * at this point.
     *
     * @param <P> Nominal type of parser constructed and returned
     * @param readCtxt Object read context to use
     *
     * @return Non-blocking parser constructed
     *
     * @throws JacksonException If parser construction or initialization fails
     */
    public <P extends JsonParser & ByteArrayFeeder> P createNonBlockingByteArrayParser(ObjectReadContext readCtxt)
            throws JacksonException {
        return _unsupported("Non-blocking source not (yet?) supported for this format ("+getFormatName()+")");
    }

    /**
     * Optional method for constructing parser for non-blocking parsing
     * via {@link ByteBufferFeeder} interface (accessed using
     * {@link JsonParser#nonBlockingInputFeeder()} from constructed instance).
     *<p>
     * If this factory does not support non-blocking parsing (either at all,
     * or from byte array),
     * will throw {@link UnsupportedOperationException}.
     *<p>
     * Note that JSON-backed factory only supports parsing of UTF-8 encoded JSON content
     * (and US-ASCII since it is proper subset); other encodings are not supported
     * at this point.
     *
     * @param <P> Nominal type of parser constructed and returned
     * @param readCtxt Object read context to use
     *
     * @return Non-blocking parser constructed
     *
     * @throws JacksonException If parser construction or initialization fails
     */
    public <P extends JsonParser & ByteBufferFeeder> P createNonBlockingByteBufferParser(ObjectReadContext readCtxt)
        throws JacksonException {
        return _unsupported("Non-blocking source not (yet?) supported for this format ("+getFormatName()+")");
    }

    /*
    /**********************************************************************
    /* Generator factories with databind context (3.0)
    /**********************************************************************
     */

    /**
     * Method for constructing generator that writes contents
     * using specified {@link OutputStream}.
     * Textual encoding used will be UTF-8, where applicable.
     *<p>
     * Underlying stream <b>is NOT owned</b> by the generator constructed,
     * so that generator will NOT close the output stream when
     * {@link JsonGenerator#close} is called (unless auto-closing
     * feature,
     * {@link tools.jackson.core.StreamWriteFeature#AUTO_CLOSE_TARGET}
     * is enabled).
     * Using application needs to close it explicitly if this is the case.
     *
     * @param writeCtxt Object-binding context where applicable; used for providing contextual
     *    configuration
     * @param out OutputStream to use for writing content
     *
     * @return Generator constructed
     *
     * @throws JacksonException If generator construction or initialization fails
     *
     * @since 3.0
     */
    public JsonGenerator createGenerator(ObjectWriteContext writeCtxt, OutputStream out)
        throws JacksonException {
        return createGenerator(writeCtxt, out, JsonEncoding.UTF8);
    }

    /**
     * Method for constructing generator that writes contents
     * using specified {@link OutputStream} with specified textual encoding
     * (if applicable).
     *<p>
     * Underlying stream <b>is NOT owned</b> by the generator constructed,
     * so that generator will NOT close the output stream when
     * {@link JsonGenerator#close} is called (unless auto-closing
     * feature,
     * {@link tools.jackson.core.StreamWriteFeature#AUTO_CLOSE_TARGET}
     * is enabled).
     * Using application needs to close it explicitly if this is the case.
     *
     * @param writeCtxt Object-binding context where applicable; used for providing contextual
     *    configuration
     * @param out OutputStream to use for writing content
     * @param enc Character set encoding to use (usually {@link JsonEncoding#UTF8})
     *
     * @return Generator constructed
     *
     * @throws JacksonException If generator construction or initialization fails
     *
     * @since 3.0
     */
    public abstract JsonGenerator createGenerator(ObjectWriteContext writeCtxt,
            OutputStream out, JsonEncoding enc)
        throws JacksonException;

    /**
     * Method for constructing generator that writes contents
     * using specified {@link Writer}.
     * Textual encoding used will be UTF-8, where applicable.
     *<p>
     * Underlying stream <b>is NOT owned</b> by the generator constructed,
     * so that generator will NOT close the Reader when
     * {@link JsonGenerator#close} is called (unless auto-closing
     * feature,
     * {@link tools.jackson.core.StreamWriteFeature#AUTO_CLOSE_TARGET} is enabled).
     * Using application needs to close it explicitly.
     *
     * @param writeCtxt Object-binding context where applicable; used for providing contextual
     *    configuration
     * @param w Writer to use for writing content
     *
     * @return Generator constructed
     *
     * @throws JacksonException If generator construction or initialization fails
     *
     * @since 3.0
     */
    public abstract JsonGenerator createGenerator(ObjectWriteContext writeCtxt, Writer w)
        throws JacksonException;

    /**
     * Method for constructing generator that writes contents
     * to specified file, overwriting contents it might have (or creating
     * it if such file does not yet exist).
     *<p>
     * Underlying stream <b>is owned</b> by the generator constructed,
     * i.e. generator will handle closing of file when
     * {@link JsonGenerator#close} is called.
     *
     * @param writeCtxt Object-binding context where applicable; used for providing contextual
     *    configuration
     * @param f File to write contents to
     * @param enc Character set encoding to use (usually {@link JsonEncoding#UTF8})
     *
     * @return Generator constructed
     *
     * @throws JacksonException If generator construction or initialization fails
     *
     * @since 3.0
     */
    public abstract JsonGenerator createGenerator(ObjectWriteContext writeCtxt, File f,
            JsonEncoding enc)
        throws JacksonException;

    /**
     * Method for constructing generator that writes contents
     * to specified path, overwriting contents it might have (or creating
     * it if such path does not yet exist).
     *<p>
     * Underlying stream <b>is owned</b> by the generator constructed,
     * i.e. generator will handle closing of path when
     * {@link JsonGenerator#close} is called.
     *
     * @param writeCtxt Object-binding context where applicable; used for providing contextual
     *    configuration
     * @param p Path to write contents to
     * @param enc Character set encoding to use (usually {@link JsonEncoding#UTF8})
     *
     * @return Generator constructed
     *
     * @throws JacksonException If generator construction or initialization fails
     *
     * @since 3.0
     */
    public abstract JsonGenerator createGenerator(ObjectWriteContext writeCtxt, Path p,
            JsonEncoding enc)
        throws JacksonException;

    /**
     * Method for constructing generator that writes content into specified {@link DataOutput},
     * using UTF-8 encoding (with formats where encoding is user-configurable).
     *
     * @param writeCtxt Object-binding context where applicable; used for providing contextual
     *    configuration
     * @param out {@link DataOutput} used as target of generation
     *
     * @return Generator constructed
     *
     * @throws JacksonException If generator construction or initialization fails
     *
     * @since 3.0
     */
    public JsonGenerator createGenerator(ObjectWriteContext writeCtxt, DataOutput out)
            throws JacksonException {
        return createGenerator(writeCtxt, _createDataOutputWrapper(out));
    }

    /*
    /**********************************************************************
    /* Generator factories, convenience methods that do not specify
    /* `ObjectWriteContext`
    /**********************************************************************
     */

    /**
     * Method for constructing JSON generator for writing content
     * using specified output stream.
     * Encoding to use must be specified, and needs to be one of available
     * types (as per JSON specification).
     *<p>
     * Underlying stream <b>is NOT owned</b> by the generator constructed,
     * so that generator will NOT close the output stream when
     * {@link JsonGenerator#close} is called (unless auto-closing
     * feature,
     * {@link tools.jackson.core.StreamWriteFeature#AUTO_CLOSE_TARGET}
     * is enabled).
     * Using application needs to close it explicitly if this is the case.
     *<p>
     * Note: there are formats that use fixed encoding (like most binary data formats)
     * and that ignore passed in encoding.
     *
     * @param out OutputStream to use for writing content
     * @param enc Character encoding to use
     *
     * @return Generator constructed
     *
     * @throws JacksonException If generator construction or initialization fails
     */
    @Deprecated // since 3.0
    public JsonGenerator createGenerator(OutputStream out, JsonEncoding enc)
        throws JacksonException {
        return createGenerator(ObjectWriteContext.empty(), out, enc);
    }

    /**
     * Convenience method for constructing generator that uses default
     * encoding of the format (UTF-8 for JSON and most other data formats),
     *
     * @param out OutputStream to use for writing content
     *
     * @return Generator constructed
     *
     * @throws JacksonException If generator construction or initialization fails
     */
    @Deprecated // since 3.0
    public JsonGenerator createGenerator(OutputStream out) throws JacksonException {
        return createGenerator(ObjectWriteContext.empty(), out, JsonEncoding.UTF8);
    }

    /**
     * Method for constructing JSON generator for writing content
     * using specified Writer.
     *<p>
     * Underlying stream <b>is NOT owned</b> by the generator constructed,
     * so that generator will NOT close the Reader when
     * {@link JsonGenerator#close} is called (unless auto-closing
     * feature,
     * {@link tools.jackson.core.StreamWriteFeature#AUTO_CLOSE_TARGET} is enabled).
     * Using application needs to close it explicitly.
     *
     * @param w Writer to use for writing content
     *
     * @return Generator constructed
     *
     * @throws JacksonException If generator construction or initialization fails
     */
    @Deprecated // since 3.0
    public JsonGenerator createGenerator(Writer w) throws JacksonException {
        return createGenerator(ObjectWriteContext.empty(), w);
    }

    /*
    /**********************************************************************
    /* Internal factory methods, other
    /**********************************************************************
     */

    /**
     * Method used by factory to create buffer recycler instances
     * for parsers and generators.
     *<p>
     * Note: only public to give access for {@code ObjectMapper}
     *
     * @return BufferRecycler instance to use
     */
    public BufferRecycler _getBufferRecycler()
    {
        return _getRecyclerPool().acquireAndLinkPooled();
    }

    /**
     * Accessor for getting access to {@link RecyclerPool} for getting
     * {@link BufferRecycler} instance to use.
     *
     * @return RecyclerPool configured for (and used by) this factory.
     */
    public RecyclerPool<BufferRecycler> _getRecyclerPool() {
        return _recyclerPool;
    }

    /**
     * Overridable factory method that actually instantiates desired
     * context object.
     *
     * @param contentRef Source reference to use (relevant to {@code TokenStreamLocation} construction)
     * @param resourceManaged Whether input/output buffers used are managed by this factory
     *
     * @return Context constructed
     */
    protected IOContext _createContext(ContentReference contentRef, boolean resourceManaged) {
        return _createContext(contentRef, resourceManaged, null);
    }

    /**
     * Overridable factory method that actually instantiates desired
     * context object.
     *
     * @param contentRef Source reference to use (relevant to {@code TokenStreamLocation} construction)
     * @param resourceManaged Whether input/output buffers used are managed by this factory
     * @param enc Character encoding defined to be used/expected
     *
     * @return Context constructed
     */
    protected IOContext _createContext(ContentReference contentRef, boolean resourceManaged,
            JsonEncoding enc)
    {
        BufferRecycler br = null;
        Object content = (contentRef == null) ? null : contentRef.getRawContent();
        // 18-Jan-2024, tatu: [core#1195] Let's see if we can reuse already allocated recycler
        //   (is the case when SegmentedStringWriter / ByteArrayBuilder passed)
        if (content instanceof BufferRecycler.Gettable) {
            br = ((BufferRecycler.Gettable) content).bufferRecycler();
        }
        boolean recyclerExternal = (br != null);
        if (!recyclerExternal) {
            br = _getBufferRecycler();
        }
        IOContext ctxt = new IOContext(_streamReadConstraints, _streamWriteConstraints,
                _errorReportConfiguration,
                br, contentRef,
                resourceManaged, enc);
        if (recyclerExternal) {
            ctxt.markBufferRecyclerReleased();
        }
        return ctxt;
    }

    /**
     * Overridable factory method for constructing {@link ContentReference}
     * to pass to parser or generator being created; used in cases where no offset
     * or length is applicable (either irrelevant, or full contents assumed).
     *
     * @param contentRef Underlying input source (parser) or target (generator)
     *
     * @return InputSourceReference instance to use
     */
    protected abstract ContentReference _createContentReference(Object contentRef);

    /**
     * Overridable factory method for constructing {@link ContentReference}
     * to pass to parser or generator being created; used in cases where input
     * comes in a static buffer with relevant offset and length.
     *
     * @param contentRef Underlying input source (parser) or target (generator)
     * @param offset Offset of content in buffer ({@code rawSource})
     * @param length Length of content in buffer ({@code rawSource})
     *
     * @return InputSourceReference instance to use
     */
    protected abstract ContentReference _createContentReference(Object contentRef,
            int offset, int length);

    protected OutputStream _createDataOutputWrapper(DataOutput out) {
        return new DataOutputAsStream(out);
    }

    /**
     * Helper method used for constructing an optimal stream for
     * parsers to use, when input is to be read from an URL.
     * This helps when reading file content via URL.
     *
     * @param url Source to read content to parse from
     *
     * @return InputStream constructed for given {@link URL}
     *
     * @throws JacksonException If there is a problem accessing content from specified {@link URL}
     *
     * @deprecated Since 3.0
     */
    @Deprecated // since 3.0
    protected InputStream _optimizedStreamFromURL(URL url) throws JacksonException {
        if ("file".equals(url.getProtocol())) {
            /* Cannot do this if the path refers
             * to a network drive on windows. This fixes the problem;
             * might not be needed on all platforms (NFS?), but should not
             * matter a lot: performance penalty of extra wrapping is more
             * relevant when accessing local file system.
             */
            String host = url.getHost();
            if (host == null || host.length() == 0) {
                // [core#48]: Let's try to avoid probs with URL encoded stuff
                String path = url.getPath();
                if (path.indexOf('%') < 0) {
                    try {
                        return new FileInputStream(url.getPath());
                    } catch (IOException e) {
                        throw _wrapIOFailure(e);
                    }
                }
                // otherwise, let's fall through and let URL decoder do its magic
            }
        }
        try {
            return url.openStream();
        } catch (IOException e) {
            throw _wrapIOFailure(e);
        }
    }

    /**
     * Helper methods used for constructing an {@link InputStream} for
     * parsers to use, when input is to be read from given {@link File}.
     *
     * @param f File to open stream for
     *
     * @return {@link InputStream} constructed
     *
     * @throws JacksonException If there is a problem opening the stream
     */
    protected InputStream _fileInputStream(File f) throws JacksonException
    {
        try {
            return new FileInputStream(f);
        } catch (IOException e) {
            throw _wrapIOFailure(e);
        }
    }

    protected InputStream _pathInputStream(Path p) throws JacksonException
    {
        try {
            return Files.newInputStream(p);
        } catch (IOException e) {
            throw _wrapIOFailure(e);
        }
    }

    /**
     * Helper methods used for constructing an {@link OutputStream} for
     * generator to use, when target is to be written into given {@link File}.
     *
     * @param f File to open stream for
     *
     * @return {@link OutputStream} constructed
     *
     * @throws JacksonException If there is a problem opening the stream
     */
    protected OutputStream _fileOutputStream(File f) throws JacksonException
    {
        try {
            return new FileOutputStream(f);
        } catch (IOException e) {
            throw _wrapIOFailure(e);
        }
    }

    protected OutputStream _pathOutputStream(Path p) throws JacksonException
    {
        try {
            return Files.newOutputStream(p);
        } catch (IOException e) {
            throw _wrapIOFailure(e);
        }
    }

    /*
    /**********************************************************************
    /* Range check helper methods
    /**********************************************************************
     */

    protected void _checkRangeBoundsForByteArray(byte[] data, int offset, int len)
        throws JacksonException
    {
        if (data == null) {
            _reportRangeError("Invalid `byte[]` argument: `null`");
        }
        final int dataLen = data.length;
        final int end = offset+len;

        // Note: we are checking that:
        //
        // !(offset < 0)
        // !(len < 0)
        // !((offset + len) < 0) // int overflow!
        // !((offset + len) > dataLen) == !((datalen - (offset+len)) < 0)

        // All can be optimized by OR'ing and checking for negative:
        int anyNegs = offset | len | end | (dataLen - end);
        if (anyNegs < 0) {
            _reportRangeError(String.format(
"Invalid 'offset' (%d) and/or 'len' (%d) arguments for `byte[]` of length %d",
offset, len, dataLen));
        }
    }

    protected void _checkRangeBoundsForCharArray(char[] data, int offset, int len)
        throws JacksonException
    {
        if (data == null) {
            _reportRangeError("Invalid `char[]` argument: `null`");
        }
        final int dataLen = data.length;
        final int end = offset+len;
        // Note: we are checking same things as with other bounds-checks
        int anyNegs = offset | len | end | (dataLen - end);
        if (anyNegs < 0) {
            _reportRangeError(String.format(
"Invalid 'offset' (%d) and/or 'len' (%d) arguments for `char[]` of length %d",
offset, len, dataLen));
        }
    }

    protected <T> T _reportRangeError(String msg) throws JacksonException
    {
        // In 2.x we used `IllegalArgumentException`: in 3.0 upgrade to StreamReadException
        throw new StreamReadException((JsonParser) null, msg);
    }

    /*
    /**********************************************************************
    /* Error reporting methods
    /**********************************************************************
     */

    protected JacksonException _wrapIOFailure(IOException e) {
        return JacksonIOException.construct(e, null);
    }

    protected <T> T _unsupported() {
        return _unsupported("Operation not supported for this format (%s)", getFormatName());
    }

    protected <T> T _unsupported(String str, Object... args) {
        throw new UnsupportedOperationException(String.format(str, args));
    }
}