| /* Jackson JSON-processor. |
| * |
| * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi |
| * |
| * Licensed under the License specified in file LICENSE, included with |
| * the source code and binary code bundles. |
| * You may not use this file except in compliance with the License. |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.codehaus.jackson; |
| |
| import java.io.*; |
| import java.lang.ref.SoftReference; |
| import java.net.URL; |
| |
| import org.codehaus.jackson.io.*; |
| import org.codehaus.jackson.impl.ByteSourceBootstrapper; |
| import org.codehaus.jackson.impl.ReaderBasedParser; |
| import org.codehaus.jackson.impl.WriterBasedGenerator; |
| import org.codehaus.jackson.sym.BytesToNameCanonicalizer; |
| import org.codehaus.jackson.sym.CharsToNameCanonicalizer; |
| import org.codehaus.jackson.util.BufferRecycler; |
| |
| /** |
| * The main factory class of Jackson package, used to configure and |
| * construct reader (aka parser, {@link JsonParser}) |
| * and 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, |
| * and since there is no need for pluggable alternative implementations |
| * (as there is no "standard" json processor API to implement), |
| * the default constructor is used for constructing factory |
| * instances. |
| * |
| * @author Tatu Saloranta |
| */ |
| public class JsonFactory |
| { |
| /** |
| * Bitfield (set of flags) of all parser features that are enabled |
| * by default. |
| */ |
| final static int DEFAULT_PARSER_FEATURE_FLAGS = JsonParser.Feature.collectDefaults(); |
| |
| /** |
| * Bitfield (set of flags) of all generator features that are enabled |
| * by default. |
| */ |
| final static int DEFAULT_GENERATOR_FEATURE_FLAGS = JsonGenerator.Feature.collectDefaults(); |
| |
| /* |
| /////////////////////////////////////////////////////// |
| // Buffer, symbol table management |
| /////////////////////////////////////////////////////// |
| */ |
| |
| /** |
| * This <code>ThreadLocal</code> contains a {@link SoftRerefence} |
| * to a {@link BufferRecycler} used to provide a low-cost |
| * buffer recycling between reader and writer instances. |
| */ |
| final static ThreadLocal<SoftReference<BufferRecycler>> _recyclerRef = new ThreadLocal<SoftReference<BufferRecycler>>(); |
| |
| /** |
| * Each factory comes equipped with a shared root symbol table. |
| * It should not be linked back to the original blueprint, to |
| * avoid contents from leaking between factories. |
| */ |
| protected CharsToNameCanonicalizer _rootCharSymbols = CharsToNameCanonicalizer.createRoot(true); |
| |
| /** |
| * Alternative to the basic symbol table, some stream-based |
| * parsers use different name canonicalization method. |
| *<p> |
| * TODO: should clean up this; looks messy having 2 alternatives |
| * with not very clear differences. |
| */ |
| protected BytesToNameCanonicalizer _rootByteSymbols = BytesToNameCanonicalizer.createRoot(); |
| |
| /* |
| /////////////////////////////////////////////////////// |
| // Configuration |
| /////////////////////////////////////////////////////// |
| */ |
| |
| /** |
| * Object that implements conversion functionality between |
| * Java objects and Json content. For base JsonFactory implementation |
| * usually not set by default, but can be explicitly set. |
| * Sub-classes (like @link org.codehaus.jackson.map.MappingJsonFactory} |
| * usually provide an implementation. |
| */ |
| protected ObjectCodec _objectCodec; |
| |
| private int _parserFeatures = DEFAULT_PARSER_FEATURE_FLAGS; |
| |
| private int _generatorFeatures = DEFAULT_GENERATOR_FEATURE_FLAGS; |
| |
| /** |
| * 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 JsonFactory() { this(null); } |
| |
| public JsonFactory(ObjectCodec oc) { _objectCodec = oc; } |
| |
| /* |
| ////////////////////////////////////////////////////// |
| // Configuration, parser settings |
| ////////////////////////////////////////////////////// |
| */ |
| |
| /** |
| * Method for enabling or disabling specified parser feature |
| * (check {@link JsonParser.Feature} for list of features) |
| * |
| * @since 1.2 |
| */ |
| public final JsonFactory configure(JsonParser.Feature f, boolean state) |
| { |
| if (state) { |
| enable(f); |
| } else { |
| disable(f); |
| } |
| return this; |
| } |
| |
| /** |
| * Method for enabling specified parser feature |
| * (check {@link JsonParser.Feature} for list of features) |
| * |
| * @since 1.2 |
| */ |
| public final JsonFactory enable(JsonParser.Feature f) { |
| _parserFeatures |= f.getMask(); |
| return this; |
| } |
| |
| /** |
| * Method for disabling specified parser features |
| * (check {@link JsonParser.Feature} for list of features) |
| * |
| * @since 1.2 |
| */ |
| public final JsonFactory disable(JsonParser.Feature f) { |
| _parserFeatures &= ~f.getMask(); |
| return this; |
| } |
| |
| /** |
| * Checked whether specified parser feature is enabled. |
| * |
| * @since 1.2 |
| */ |
| public final boolean isEnabled(JsonParser.Feature f) { |
| return (_parserFeatures & f.getMask()) != 0; |
| } |
| |
| // // // Older deprecated (as of 1.2) methods |
| |
| /** |
| * @deprecated Use {@link #enable(JsonParser.Feature)} instead |
| */ |
| public final void enableParserFeature(JsonParser.Feature f) { |
| enable(f); |
| } |
| |
| /** |
| * @deprecated Use {@link #disable(JsonParser.Feature)} instead |
| */ |
| public final void disableParserFeature(JsonParser.Feature f) { |
| disable(f); |
| } |
| |
| /** |
| * @deprecated Use {@link #configure(JsonParser.Feature, boolean)} instead |
| */ |
| public final void setParserFeature(JsonParser.Feature f, boolean state) { |
| configure(f, state); |
| } |
| |
| /** |
| * @deprecated Use {@link #isEnabled(JsonParser.Feature)} instead |
| */ |
| public final boolean isParserFeatureEnabled(JsonParser.Feature f) { |
| return (_parserFeatures & f.getMask()) != 0; |
| } |
| |
| /* |
| ////////////////////////////////////////////////////// |
| // Configuration, generator settings |
| ////////////////////////////////////////////////////// |
| */ |
| |
| /** |
| * Method for enabling or disabling specified generator feature |
| * (check {@link JsonGenerator.Feature} for list of features) |
| * |
| * @since 1.2 |
| */ |
| public final JsonFactory configure(JsonGenerator.Feature f, boolean state) { |
| if (state) { |
| enable(f); |
| } else { |
| disable(f); |
| } |
| return this; |
| } |
| |
| |
| /** |
| * Method for enabling specified generator features |
| * (check {@link JsonGenerator.Feature} for list of features) |
| * |
| * @since 1.2 |
| */ |
| public final JsonFactory enable(JsonGenerator.Feature f) { |
| _generatorFeatures |= f.getMask(); |
| return this; |
| } |
| |
| /** |
| * Method for disabling specified generator feature |
| * (check {@link JsonGenerator.Feature} for list of features) |
| * |
| * @since 1.2 |
| */ |
| public final JsonFactory disable(JsonGenerator.Feature f) { |
| _generatorFeatures &= ~f.getMask(); |
| return this; |
| } |
| |
| /** |
| * Checked whether specified generator feature is enabled. |
| * |
| * @since 1.2 |
| */ |
| public final boolean isEnabled(JsonGenerator.Feature f) { |
| return (_generatorFeatures & f.getMask()) != 0; |
| } |
| |
| // // // Older deprecated (as of 1.2) methods |
| |
| /** |
| * @deprecated Use {@link #enable(JsonGenerator.Feature)} instead |
| */ |
| public final void enableGeneratorFeature(JsonGenerator.Feature f) { |
| enable(f); |
| } |
| |
| /** |
| * @deprecated Use {@link #disable(JsonGenerator.Feature)} instead |
| */ |
| public final void disableGeneratorFeature(JsonGenerator.Feature f) { |
| disable(f); |
| } |
| |
| /** |
| * @deprecated Use {@link #configure(JsonGenerator.Feature, boolean)} instead |
| */ |
| public final void setGeneratorFeature(JsonGenerator.Feature f, boolean state) { |
| configure(f, state); |
| } |
| |
| /** |
| * @deprecated Use {@link #isEnabled(JsonGenerator.Feature)} instead |
| */ |
| public final boolean isGeneratorFeatureEnabled(JsonGenerator.Feature f) { |
| return isEnabled(f); |
| } |
| |
| /* |
| ////////////////////////////////////////////////////// |
| // Configuration, other |
| ////////////////////////////////////////////////////// |
| */ |
| |
| public JsonFactory setCodec(ObjectCodec oc) { |
| _objectCodec = oc; |
| return this; |
| } |
| |
| public ObjectCodec getCodec() { return _objectCodec; } |
| |
| /* |
| ////////////////////////////////////////////////////// |
| // Reader factories |
| ////////////////////////////////////////////////////// |
| */ |
| |
| /** |
| * Method for constructing json parser instance to parse |
| * contents of specified file. Encoding is auto-detected |
| * from contents according to json specification recommended |
| * mechanism. |
| *<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 f File that contains JSON content to parse |
| */ |
| public final JsonParser createJsonParser(File f) |
| throws IOException, JsonParseException |
| { |
| return _createJsonParser(new FileInputStream(f), _createContext(f, true)); |
| } |
| |
| /** |
| * Method for constructing json parser instance to parse |
| * contents of resource reference by given URL. |
| * Encoding is auto-detected |
| * from contents according to json specification recommended |
| * mechanism. |
| *<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 url URL pointing to resource that contains JSON content to parse |
| */ |
| public final JsonParser createJsonParser(URL url) |
| throws IOException, JsonParseException |
| { |
| return _createJsonParser(_optimizedStreamFromURL(url), _createContext(url, true)); |
| } |
| |
| /** |
| * 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 org.codehaus.jackson.JsonParser.Feature#AUTO_CLOSE_SOURCE} |
| * is enabled. |
| *<p> |
| * Note: no encoding argument is taken since it can always be |
| * auto-detected as suggested by Json RFC. |
| * |
| * @param in InputStream to use for reading JSON content to parse |
| */ |
| public final JsonParser createJsonParser(InputStream in) |
| throws IOException, JsonParseException |
| { |
| return _createJsonParser(in, _createContext(in, false)); |
| } |
| |
| /** |
| * Method for constructing json parser instance to parse |
| * 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 org.codehaus.jackson.JsonParser.Feature#AUTO_CLOSE_SOURCE} |
| * is enabled. |
| *<p> |
| * |
| * @param r Reader to use for reading JSON content to parse |
| */ |
| public final JsonParser createJsonParser(Reader r) |
| throws IOException, JsonParseException |
| { |
| return new ReaderBasedParser(_createContext(r, false), _parserFeatures, r, _objectCodec, |
| _rootCharSymbols.makeChild(isEnabled(JsonParser.Feature.INTERN_FIELD_NAMES))); |
| } |
| |
| public final JsonParser createJsonParser(byte[] data) |
| throws IOException, JsonParseException |
| { |
| return createJsonParser(data, 0, data.length); |
| } |
| |
| public final JsonParser createJsonParser(byte[] data, int offset, int len) |
| throws IOException, JsonParseException |
| { |
| // true -> managed (doesn't really matter; we have no stream!) |
| IOContext ctxt = _createContext(data, true); |
| return new ByteSourceBootstrapper(ctxt, data, offset, len).constructParser(_parserFeatures, _objectCodec, _rootByteSymbols, _rootCharSymbols); |
| } |
| |
| public final JsonParser createJsonParser(String content) |
| throws IOException, JsonParseException |
| { |
| StringReader r = new StringReader(content); |
| // true -> must be managed as caller didn't hand Reader |
| return new ReaderBasedParser(_createContext(r, true), _parserFeatures, r, _objectCodec, |
| _rootCharSymbols.makeChild(isEnabled(JsonParser.Feature.INTERN_FIELD_NAMES))); |
| } |
| |
| private JsonParser _createJsonParser(InputStream in, IOContext ctxt) |
| throws IOException, JsonParseException |
| { |
| return new ByteSourceBootstrapper(ctxt, in).constructParser(_parserFeatures, _objectCodec, _rootByteSymbols, _rootCharSymbols); |
| } |
| |
| /* |
| ////////////////////////////////////////////////////// |
| // Generator factories |
| ////////////////////////////////////////////////////// |
| */ |
| |
| /** |
| * Method for constructing json generator for writing json 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 org.codehaus.jackson.JsonGenerator.Feature#AUTO_CLOSE_TARGET} |
| * is enabled). |
| * Using application needs to close it explicitly if this is the case. |
| * |
| * @param out OutputStream to use for writing json content |
| * @param enc Character encoding to use |
| */ |
| public final JsonGenerator createJsonGenerator(OutputStream out, JsonEncoding enc) |
| throws IOException |
| { |
| IOContext ctxt = _createContext(out, false); |
| ctxt.setEncoding(enc); |
| if (enc == JsonEncoding.UTF8) { // We have optimized writer for UTF-8 |
| return new WriterBasedGenerator(ctxt, _generatorFeatures, _objectCodec, new UTF8Writer(ctxt, out)); |
| } |
| return new WriterBasedGenerator(ctxt, _generatorFeatures, _objectCodec, new OutputStreamWriter(out, enc.getJavaName())); |
| } |
| |
| /** |
| * Method for constructing json generator for writing json 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 org.codehaus.jackson.JsonGenerator.Feature#AUTO_CLOSE_TARGET} is enabled). |
| * Using application needs to close it explicitly. |
| * |
| * @param out Writer to use for writing json content |
| */ |
| public final JsonGenerator createJsonGenerator(Writer out) |
| throws IOException |
| { |
| IOContext ctxt = _createContext(out, false); |
| return new WriterBasedGenerator(ctxt, _generatorFeatures, _objectCodec, out); |
| } |
| |
| /** |
| * Method for constructing json generator for writing json content |
| * to specified file, overwriting contents it might have (or creating |
| * it if such file does not yet exist). |
| * Encoding to use must be specified, and needs to be one of available |
| * types (as per JSON specification). |
| *<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 f File to write contents to |
| * @param enc Character encoding to use |
| */ |
| public final JsonGenerator createJsonGenerator(File f, JsonEncoding enc) |
| throws IOException |
| { |
| return createJsonGenerator(new FileOutputStream(f), enc); |
| } |
| |
| /* |
| /////////////////////////////////////////////////////////// |
| // Internal methods |
| /////////////////////////////////////////////////////////// |
| */ |
| |
| /** |
| * Method used by the factory to create parsing context for parser |
| * instances. |
| */ |
| protected final IOContext _createContext(Object srcRef, boolean resourceManaged) |
| { |
| return new IOContext(_getBufferRecycler(), srcRef, resourceManaged); |
| } |
| |
| /** |
| * Method used by factory to create buffer recycler instances |
| * for parsers and generators. |
| *<p> |
| * Note: only public to give access for <code>ObjectMapper</code> |
| */ |
| public final BufferRecycler _getBufferRecycler() |
| { |
| SoftReference<BufferRecycler> ref = _recyclerRef.get(); |
| BufferRecycler br = (ref == null) ? null : ref.get(); |
| |
| if (br == null) { |
| br = new BufferRecycler(); |
| if (ref == null) { |
| _recyclerRef.set(new SoftReference<BufferRecycler>(br)); |
| } |
| } |
| return br; |
| } |
| |
| /** |
| * Helper methods 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. |
| */ |
| protected final static InputStream _optimizedStreamFromURL(URL url) |
| throws IOException |
| { |
| if ("file".equals(url.getProtocol())) { |
| /* Can not 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) { |
| return new FileInputStream(url.getPath()); |
| } |
| } |
| return url.openStream(); |
| } |
| } |