package com.fasterxml.jackson.dataformat.smile;

import java.io.*;
import java.net.URL;

import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.format.InputAccessor;
import com.fasterxml.jackson.core.format.MatchStrength;
import com.fasterxml.jackson.core.io.IOContext;
import com.fasterxml.jackson.core.sym.ByteQuadsCanonicalizer;
import com.fasterxml.jackson.dataformat.smile.async.NonBlockingByteArrayParser;

Factory used for constructing SmileParser and SmileGenerator instances; both of which handle Smile encoded data.

Extends JsonFactory mostly so that users can actually use it in place of regular non-Smile factory instances.

Note on using non-byte-based sources/targets (char based, like Reader and Writer): these can not be used for Smile-format documents, and thus will either downgrade to textual JSON (when parsing), or throw exception (when trying to create generator).

Author:Tatu Saloranta
/** * Factory used for constructing {@link SmileParser} and {@link SmileGenerator} * instances; both of which handle * <a href="http://wiki.fasterxml.com/SmileFormat">Smile</a> encoded data. *<p> * Extends {@link JsonFactory} mostly so that users can actually use it in place * of regular non-Smile factory instances. *<p> * Note on using non-byte-based sources/targets (char based, like * {@link java.io.Reader} and {@link java.io.Writer}): these can not be * used for Smile-format documents, and thus will either downgrade to * textual JSON (when parsing), or throw exception (when trying to create * generator). * * @author Tatu Saloranta */
public class SmileFactory extends JsonFactory { private static final long serialVersionUID = 1L; // since 2.6 /* /********************************************************** /* Constants /********************************************************** */
Name used to identify Smile format. (and returned by getFormatName()
/** * Name used to identify Smile format. * (and returned by {@link #getFormatName()} */
public final static String FORMAT_NAME_SMILE = "Smile";
Bitfield (set of flags) of all parser features that are enabled by default.
/** * Bitfield (set of flags) of all parser features that are enabled * by default. */
final static int DEFAULT_SMILE_PARSER_FEATURE_FLAGS = SmileParser.Feature.collectDefaults();
Bitfield (set of flags) of all generator features that are enabled by default.
/** * Bitfield (set of flags) of all generator features that are enabled * by default. */
final static int DEFAULT_SMILE_GENERATOR_FEATURE_FLAGS = SmileGenerator.Feature.collectDefaults(); /* /********************************************************** /* Configuration /********************************************************** */
Whether non-supported methods (ones trying to output using char-based targets like Writer, for example) should be delegated to regular Jackson JSON processing (if set to true); or throw UnsupportedOperationException (if set to false)
/** * Whether non-supported methods (ones trying to output using * char-based targets like {@link java.io.Writer}, for example) * should be delegated to regular Jackson JSON processing * (if set to true); or throw {@link UnsupportedOperationException} * (if set to false) */
protected boolean _cfgDelegateToTextual; protected int _smileParserFeatures; protected int _smileGeneratorFeatures; /* /********************************************************** /* 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.
/** * 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 SmileFactory() { this(null); } public SmileFactory(ObjectCodec oc) { super(oc); _smileParserFeatures = DEFAULT_SMILE_PARSER_FEATURE_FLAGS; _smileGeneratorFeatures = DEFAULT_SMILE_GENERATOR_FEATURE_FLAGS; }
Note: REQUIRES 2.2.1 -- unfortunate intra-patch dep but seems preferable to just leaving bug be as is
Since:2.2.1
/** * Note: REQUIRES 2.2.1 -- unfortunate intra-patch dep but seems * preferable to just leaving bug be as is * * @since 2.2.1 */
public SmileFactory(SmileFactory src, ObjectCodec oc) { super(src, oc); _cfgDelegateToTextual = src._cfgDelegateToTextual; _smileParserFeatures = src._smileParserFeatures; _smileGeneratorFeatures = src._smileGeneratorFeatures; } // @since 2.1 @Override public SmileFactory copy() { _checkInvalidCopy(SmileFactory.class); // note: as with base class, must NOT copy mapper reference return new SmileFactory(this, null); } public void delegateToTextual(boolean state) { _cfgDelegateToTextual = state; } /* /********************************************************** /* 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.
/** * Method that we need to override to actually make restoration go * through constructors etc. * Also: must be overridden by sub-classes as well. */
@Override protected Object readResolve() { return new SmileFactory(this, _objectCodec); } /* /********************************************************** /* Versioned /********************************************************** */ @Override public Version version() { return PackageVersion.VERSION; } /* /********************************************************** /* Format detection functionality /********************************************************** */ @Override public String getFormatName() { return FORMAT_NAME_SMILE; } // Defaults work fine for this: // public boolean canUseSchema(FormatSchema schema) { }
Sub-classes need to override this method (as of 1.8)
/** * Sub-classes need to override this method (as of 1.8) */
@Override public MatchStrength hasFormat(InputAccessor acc) throws IOException { return SmileParserBootstrapper.hasSmileFormat(acc); } /* /********************************************************** /* Capability introspection /********************************************************** */ @Override public boolean canUseCharArrays() { return false; } @Override public boolean canHandleBinaryNatively() { return true; } @Override // since 2.9 public boolean canParseAsync() { return true; } @Override // since 2.6 public Class<SmileParser.Feature> getFormatReadFeatureType() { return SmileParser.Feature.class; } @Override // since 2.6 public Class<SmileGenerator.Feature> getFormatWriteFeatureType() { return SmileGenerator.Feature.class; } /* /********************************************************** /* Configuration, parser settings /********************************************************** */
Method for enabling or disabling specified parser feature (check Feature for list of features)
/** * Method for enabling or disabling specified parser feature * (check {@link SmileParser.Feature} for list of features) */
public final SmileFactory configure(SmileParser.Feature f, boolean state) { if (state) { enable(f); } else { disable(f); } return this; }
Method for enabling specified parser feature (check Feature for list of features)
/** * Method for enabling specified parser feature * (check {@link SmileParser.Feature} for list of features) */
public SmileFactory enable(SmileParser.Feature f) { _smileParserFeatures |= f.getMask(); return this; }
Method for disabling specified parser features (check Feature for list of features)
/** * Method for disabling specified parser features * (check {@link SmileParser.Feature} for list of features) */
public SmileFactory disable(SmileParser.Feature f) { _smileParserFeatures &= ~f.getMask(); return this; }
Checked whether specified parser feature is enabled.
/** * Checked whether specified parser feature is enabled. */
public final boolean isEnabled(SmileParser.Feature f) { return (_smileParserFeatures & f.getMask()) != 0; } /* /********************************************************** /* Configuration, generator settings /********************************************************** */
Method for enabling or disabling specified generator feature (check Feature for list of features)
Since:1.2
/** * Method for enabling or disabling specified generator feature * (check {@link SmileGenerator.Feature} for list of features) * * @since 1.2 */
public final SmileFactory configure(SmileGenerator.Feature f, boolean state) { if (state) { enable(f); } else { disable(f); } return this; }
Method for enabling specified generator features (check Feature for list of features)
/** * Method for enabling specified generator features * (check {@link SmileGenerator.Feature} for list of features) */
public SmileFactory enable(SmileGenerator.Feature f) { _smileGeneratorFeatures |= f.getMask(); return this; }
Method for disabling specified generator feature (check Feature for list of features)
/** * Method for disabling specified generator feature * (check {@link SmileGenerator.Feature} for list of features) */
public SmileFactory disable(SmileGenerator.Feature f) { _smileGeneratorFeatures &= ~f.getMask(); return this; }
Check whether specified generator feature is enabled.
/** * Check whether specified generator feature is enabled. */
public final boolean isEnabled(SmileGenerator.Feature f) { return (_smileGeneratorFeatures & f.getMask()) != 0; } /* /********************************************************** /* Overridden parser factory methods: only override methods /* that can use co-variance (to return SmileParser) /********************************************************** */ @SuppressWarnings("resource") @Override public SmileParser createParser(File f) throws IOException { IOContext ctxt = _createContext(f, true); return _createParser(_decorate(new FileInputStream(f), ctxt), ctxt); } @Override public SmileParser createParser(URL url) throws IOException { IOContext ctxt = _createContext(url, true); return _createParser(_decorate(_optimizedStreamFromURL(url), ctxt), ctxt); } @Override public SmileParser createParser(InputStream in) throws IOException { IOContext ctxt = _createContext(in, false); return _createParser(_decorate(in, ctxt), ctxt); } @Override public SmileParser createParser(byte[] data) throws IOException { return createParser(data, 0, data.length); } @SuppressWarnings("resource") @Override public SmileParser createParser(byte[] data, int offset, int len) throws IOException { IOContext ctxt = _createContext(data, true); if (_inputDecorator != null) { InputStream in = _inputDecorator.decorate(ctxt, data, 0, data.length); if (in != null) { return _createParser(_decorate(in, ctxt), ctxt); } } return _createParser(data, offset, len, ctxt); } /* /********************************************************** /* Overridden generator factory methods: mostly /* overridden for co-variance (returns SmileGenerator) /********************************************************** */
Method for constructing JsonGenerator for generating Smile-encoded output.

Since Smile format always uses UTF-8 internally, enc argument is ignored.

/** * Method for constructing {@link JsonGenerator} for generating * Smile-encoded output. *<p> * Since Smile format always uses UTF-8 internally, <code>enc</code> * argument is ignored. */
@Override public SmileGenerator createGenerator(OutputStream out, JsonEncoding enc) throws IOException { // false -> we won't manage the stream unless explicitly directed to IOContext ctxt = _createContext(out, false); return _createGenerator(_decorate(out, ctxt), ctxt); }
Method for constructing JsonGenerator for generating Smile-encoded output.

Since Smile format always uses UTF-8 internally, no encoding need to be passed to this method.

/** * Method for constructing {@link JsonGenerator} for generating * Smile-encoded output. *<p> * Since Smile format always uses UTF-8 internally, no encoding need * to be passed to this method. */
@Override public SmileGenerator createGenerator(OutputStream out) throws IOException { // false -> we won't manage the stream unless explicitly directed to IOContext ctxt = _createContext(out, false); return _createGenerator(_decorate(out, ctxt), ctxt); } /* /********************************************************** /* Experimental extended factory method(s) for creating /* non-blocking parsers /********************************************************** */
Since:2.9
/** * @since 2.9 */
@Override public NonBlockingByteArrayParser createNonBlockingByteArrayParser() throws IOException { IOContext ctxt = _createContext(null, false); ByteQuadsCanonicalizer can = _byteSymbolCanonicalizer.makeChild(_factoryFeatures); return new NonBlockingByteArrayParser(ctxt, _parserFeatures, _smileParserFeatures, can); } /* /****************************************************** /* Overridden internal factory methods /****************************************************** */
Overridable factory method that actually instantiates desired parser.
/** * Overridable factory method that actually instantiates desired parser. */
@Override protected SmileParser _createParser(InputStream in, IOContext ctxt) throws IOException { SmileParserBootstrapper bs = new SmileParserBootstrapper(ctxt, in); return bs.constructParser(_factoryFeatures, _parserFeatures, _smileParserFeatures, _objectCodec, _byteSymbolCanonicalizer); } @Override protected JsonParser _createParser(Reader r, IOContext ctxt) throws IOException { if (_cfgDelegateToTextual) { return super._createParser(r, ctxt); } return _nonByteSource(); } @Override protected JsonParser _createParser(char[] data, int offset, int len, IOContext ctxt, boolean recyclable) throws IOException { if (_cfgDelegateToTextual) { return super._createParser(data, offset, len, ctxt, recyclable); } return _nonByteSource(); } @Override protected SmileParser _createParser(byte[] data, int offset, int len, IOContext ctxt) throws IOException { return new SmileParserBootstrapper(ctxt, data, offset, len).constructParser( _factoryFeatures, _parserFeatures, _smileParserFeatures, _objectCodec, _byteSymbolCanonicalizer); } @Override protected JsonGenerator _createGenerator(Writer out, IOContext ctxt) throws IOException { if (_cfgDelegateToTextual) { return super._createGenerator(out, ctxt); } return _nonByteTarget(); } @Override protected JsonGenerator _createUTF8Generator(OutputStream out, IOContext ctxt) throws IOException { return _createGenerator(out, ctxt); } //public BufferRecycler _getBufferRecycler() @Override protected Writer _createWriter(OutputStream out, JsonEncoding enc, IOContext ctxt) throws IOException { if (_cfgDelegateToTextual) { return super._createWriter(out, enc, ctxt); } return _nonByteTarget(); } /* /********************************************************** /* Internal methods /********************************************************** */ protected <T> T _nonByteSource() throws IOException { throw new UnsupportedOperationException("Can not create parser for character-based (not byte-based) source"); } protected <T> T _nonByteTarget() throws IOException { throw new UnsupportedOperationException("Can not create generator for character-based (not byte-based) target"); } protected SmileGenerator _createGenerator(OutputStream out, IOContext ctxt) throws IOException { int feats = _smileGeneratorFeatures; /* One sanity check: MUST write header if shared string values setting is enabled, * or quoting of binary data disabled. * But should we force writing, or throw exception, if settings are in conflict? * For now, let's error out... */ SmileGenerator gen = new SmileGenerator(ctxt, _generatorFeatures, feats, _objectCodec, out); if ((feats & SmileGenerator.Feature.WRITE_HEADER.getMask()) != 0) { gen.writeHeader(); } else { if ((feats & SmileGenerator.Feature.CHECK_SHARED_STRING_VALUES.getMask()) != 0) { throw new JsonGenerationException( "Inconsistent settings: WRITE_HEADER disabled, but CHECK_SHARED_STRING_VALUES enabled; can not construct generator" +" due to possible data loss (either enable WRITE_HEADER, or disable CHECK_SHARED_STRING_VALUES to resolve)", gen); } if ((feats & SmileGenerator.Feature.ENCODE_BINARY_AS_7BIT.getMask()) == 0) { throw new JsonGenerationException( "Inconsistent settings: WRITE_HEADER disabled, but ENCODE_BINARY_AS_7BIT disabled; can not construct generator" +" due to possible data loss (either enable WRITE_HEADER, or ENCODE_BINARY_AS_7BIT to resolve)", gen); } } return gen; } }