/** * 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 { /** Name used to identify Smile format. (and returned by {@link #getFormatName()} */ public static final String FORMAT_NAME_SMILE = "Smile"; /** Bitfield (set of flags) of all parser features that are enabled by default. */ static final int DEFAULT_SMILE_PARSER_FEATURE_FLAGS = SmileParser.Feature.collectDefaults(); /** Bitfield (set of flags) of all generator features that are enabled by default. */ static final int DEFAULT_SMILE_GENERATOR_FEATURE_FLAGS = SmileGenerator.Feature.collectDefaults(); /* /********************************************************** /* Configuration /********************************************************** */ /** * 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 = DEFAULT_SMILE_PARSER_FEATURE_FLAGS; protected int _smileGeneratorFeatures = DEFAULT_SMILE_GENERATOR_FEATURE_FLAGS; /* /********************************************************** /* 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 SmileFactory() { this(null); } public SmileFactory(ObjectCodec oc) { super(oc); } // @since 2.1 @Override public SmileFactory copy() { _checkInvalidCopy(SmileFactory.class); // note: as with base class, must NOT copy mapper reference return new SmileFactory(null); } public void delegateToTextual(boolean state) { _cfgDelegateToTextual = state; } /* /********************************************************** /* Versioned /********************************************************** */ @Override public Version version() { return ModuleVersion.instance.version(); } /* /********************************************************** /* Format detection functionality (since 1.8) /********************************************************** */ @Override public String getFormatName() { return FORMAT_NAME_SMILE; } /** Sub-classes need to override this method (as of 1.8) */ @Override public MatchStrength hasFormat(InputAccessor acc) throws IOException { return SmileParserBootstrapper.hasSmileFormat(acc); } /* /********************************************************** /* Configuration, parser settings /********************************************************** */ /** * 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 {@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 {@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. */ public final boolean isEnabled(SmileParser.Feature f) { return (_smileParserFeatures & f.getMask()) != 0; } /* /********************************************************** /* Configuration, generator settings /********************************************************** */ /** * 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 {@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 {@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. */ public final boolean isEnabled(SmileGenerator.Feature f) { return (_smileGeneratorFeatures & f.getMask()) != 0; } /* /********************************************************** /* Overridden parser factory methods, new (2.1) /********************************************************** */ @Override public SmileParser createParser(File f) throws IOException, JsonParseException { return _createParser(new FileInputStream(f), _createContext(f, true)); } @Override public SmileParser createParser(URL url) throws IOException, JsonParseException { return _createParser(_optimizedStreamFromURL(url), _createContext(url, true)); } @Override public SmileParser createParser(InputStream in) throws IOException, JsonParseException { return _createParser(in, _createContext(in, false)); } // public JsonParser createJsonParser(Reader r) @Override public SmileParser createParser(byte[] data) throws IOException, JsonParseException { IOContext ctxt = _createContext(data, true); return _createParser(data, 0, data.length, ctxt); } @Override public SmileParser createParser(byte[] data, int offset, int len) throws IOException, JsonParseException { return _createParser(data, offset, len, _createContext(data, true)); } /* /********************************************************** /* Overridden parser factory methods, old (pre-2.1) /********************************************************** */ /** * @deprecated Since 2.1 Use {@link #createParser(File)} instead * @since 2.1 */ @Deprecated @Override public SmileParser createJsonParser(File f) throws IOException, JsonParseException { return _createParser(new FileInputStream(f), _createContext(f, true)); } /** * @deprecated Since 2.1 Use {@link #createParser(URL)} instead * @since 2.1 */ @Deprecated @Override public SmileParser createJsonParser(URL url) throws IOException, JsonParseException { return _createParser(_optimizedStreamFromURL(url), _createContext(url, true)); } /** * @deprecated Since 2.1 Use {@link #createParser(InputStream)} instead * @since 2.1 */ @Deprecated @Override public SmileParser createJsonParser(InputStream in) throws IOException, JsonParseException { return _createParser(in, _createContext(in, false)); } // public JsonParser createJsonParser(Reader r) /** * @deprecated Since 2.1 Use {@link #createParser(byte[])} instead * @since 2.1 */ @Deprecated @Override public SmileParser createJsonParser(byte[] data) throws IOException, JsonParseException { IOContext ctxt = _createContext(data, true); return _createParser(data, 0, data.length, ctxt); } /** * @deprecated Since 2.1 Use {@link #createParser(byte[],int,int)} instead * @since 2.1 */ @Deprecated @Override public SmileParser createJsonParser(byte[] data, int offset, int len) throws IOException, JsonParseException { return _createParser(data, offset, len, _createContext(data, true)); } /* /********************************************************** /* Overridden generator factory methods, new (2.1) /********************************************************** */ /** * 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 return _createGenerator(out, _createContext(out, false)); } /** * 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 return _createGenerator(out, _createContext(out, false)); } /* /********************************************************** /* Overridden generator factory methods, old (pre-2.1) /********************************************************** */ /** * @deprecated Since 2.1 Use {@link #createGenerator(OutputStream)} instead * @since 2.1 */ @Deprecated @Override public SmileGenerator createJsonGenerator(OutputStream out, JsonEncoding enc) throws IOException { // false -> we won't manage the stream unless explicitly directed to return _createGenerator(out, _createContext(out, false)); } /** * @deprecated Since 2.1 Use {@link #createGenerator(OutputStream)} instead * @since 2.1 */ @Deprecated @Override public SmileGenerator createJsonGenerator(OutputStream out) throws IOException { // false -> we won't manage the stream unless explicitly directed to IOContext ctxt = _createContext(out, false); return _createGenerator(out, ctxt); } @Deprecated @Override protected SmileGenerator _createUTF8JsonGenerator(OutputStream out, IOContext ctxt) throws IOException { return _createGenerator(out, ctxt); } /* /****************************************************** /* Overridden internal factory methods /****************************************************** */ // protected IOContext _createContext(Object srcRef, boolean resourceManaged) /** Overridable factory method that actually instantiates desired parser. */ @Override protected SmileParser _createParser(InputStream in, IOContext ctxt) throws IOException, JsonParseException { return new SmileParserBootstrapper(ctxt, in) .constructParser( _parserFeatures, _smileParserFeatures, isEnabled(JsonFactory.Feature.INTERN_FIELD_NAMES), _objectCodec, _rootByteSymbols); } /** Overridable factory method that actually instantiates desired parser. */ @Override protected JsonParser _createParser(Reader r, IOContext ctxt) throws IOException, JsonParseException { if (_cfgDelegateToTextual) { return super._createParser(r, ctxt); } throw new UnsupportedOperationException("Can not create generator for non-byte-based target"); } /** Overridable factory method that actually instantiates desired parser. */ @Override protected SmileParser _createParser(byte[] data, int offset, int len, IOContext ctxt) throws IOException, JsonParseException { return new SmileParserBootstrapper(ctxt, data, offset, len) .constructParser( _parserFeatures, _smileParserFeatures, isEnabled(JsonFactory.Feature.INTERN_FIELD_NAMES), _objectCodec, _rootByteSymbols); } /** Overridable factory method that actually instantiates desired generator. */ @Override protected JsonGenerator _createGenerator(Writer out, IOContext ctxt) throws IOException { if (_cfgDelegateToTextual) { return super._createGenerator(out, ctxt); } throw new UnsupportedOperationException("Can not create generator for non-byte-based target"); } @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); } throw new UnsupportedOperationException("Can not create generator for non-byte-based target"); } /* /********************************************************** /* Internal methods /********************************************************** */ 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)"); } 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)"); } } return gen; } }
/** * 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. */ public final boolean isEnabled(SmileGenerator.Feature f) { return (_smileGeneratorFeatures & f.getMask()) != 0; }
/** * 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; }