src.java.org.codehaus.jackson.JsonFactory Maven / Gradle / Ivy
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.NameCanonicalizer;
import org.codehaus.jackson.util.BufferRecycler;
import org.codehaus.jackson.util.SymbolTable;
/**
* JsonFactory is a thread-safe reusable provider of
* parser and generator instances. After
*/
public final class JsonFactory
{
/**
* This ThreadLocal
contains a {@link SoftRerefence}
* to a {@link BufferRecycler} used to provide a low-cost
* buffer recycling between reader and writer instances.
*/
final static ThreadLocal> mRecyclerRef = new ThreadLocal>();
/**
* 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.
*/
private SymbolTable mCharSymbols = SymbolTable.createRoot();
/**
* Alternative to the basic symbol table, some stream-based
* parsers use different name canonicalization method.
*
* TODO: should clean up this; looks messy having 2 alternatives
* with not very clear differences.
*/
private NameCanonicalizer mByteSymbols = NameCanonicalizer.createRoot();
/**
* Creation of a factory instance is quite light-weight operation,
* and since there is no need for pluggable alternative implementations
* (since there is no "standard" json processor API to implement),
* default constructor is used for constructing factories.
* Also, there is no separation between parser and generator
* construction.
*/
public JsonFactory() { }
/*
//////////////////////////////////////////////////////
// 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.
*
* Underlying input stream (needed for reading contents)
* will be owned (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 JsonParser createJsonParser(File f)
throws IOException, JsonParseException
{
return createJsonParser(new FileInputStream(f), createContext(f));
}
/**
* 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.
*
* Underlying input stream (needed for reading contents)
* will be owned (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 JsonParser createJsonParser(URL url)
throws IOException, JsonParseException
{
return createJsonParser(optimizedStreamFromURL(url), createContext(url));
}
/**
* Method for constructing json parser instance to parse
* the contents accessed via specified input stream.
*
* Input stream will NOT be owned (not managed) by
* the parser, since caller does have access to it and
* is expected to close it if and as necessary.
*
* 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 JsonParser createJsonParser(InputStream in)
throws IOException, JsonParseException
{
return createJsonParser(in, createContext(in));
}
/**
* Method for constructing json parser instance to parse
* the contents accessed via specified Reader.
*
* Reader will NOT be owned (not managed) by
* the parser, since caller does have access to it and
* is expected to close it if and as necessary.
*
* @param r Reader to use for reading JSON content to parse
*/
public JsonParser createJsonParser(Reader r)
throws IOException, JsonParseException
{
return new ReaderBasedParser(createContext(r), r, mCharSymbols.makeChild());
}
private JsonParser createJsonParser(InputStream in, IOContext ctxt)
throws IOException, JsonParseException
{
ByteSourceBootstrapper bb = new ByteSourceBootstrapper(ctxt, in);
JsonEncoding enc = bb.detectEncoding();
if (enc == JsonEncoding.UTF8) {
return bb.createFastUtf8Parser(mByteSymbols.makeChild());
}
return new ReaderBasedParser(ctxt, bb.constructReader(), mCharSymbols.makeChild());
}
/*
//////////////////////////////////////////////////////
// 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).
*
* Underlying stream is NOT owned by the generator constructed,
* so that generator will NOT close the output stream when
* {@link JsonGenerator#close} is called.
* Using application needs to close it explicitly.
*
* @param out OutputStream to use for writing json content
* @param enc Character encoding to use
*/
public JsonGenerator createJsonGenerator(OutputStream out, JsonEncoding enc)
throws IOException
{
IOContext ctxt = createContext(out);
ctxt.setEncoding(enc);
if (enc == JsonEncoding.UTF8) { // We have optimized writer for UTF-8
return new WriterBasedGenerator(ctxt, new UTF8Writer(ctxt, out));
}
return new WriterBasedGenerator(ctxt, new OutputStreamWriter(out, enc.getJavaName()));
}
/**
* Method for constructing json generator for writing json content
* using specified Writer.
*
* Underlying stream is NOT owned by the generator constructed,
* so that generator will NOT close the Reader when
* {@link JsonGenerator#close} is called. Using application
* needs to close Writer explicitly.
*
* @param out Writer to use for writing json content
*/
public JsonGenerator createJsonGenerator(Writer out)
throws IOException
{
IOContext ctxt = createContext(out);
return new WriterBasedGenerator(ctxt, 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).
*
* Underlying stream is owned 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 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 IOContext createContext(Object srcRef)
{
return new IOContext(getBufferRecycler(), srcRef);
}
/**
* Method used by factory to create buffer recycler instances
* for parsers and generators.
*/
protected BufferRecycler getBufferRecycler()
{
SoftReference ref = mRecyclerRef.get();
BufferRecycler br = (ref == null) ? null : ref.get();
if (br == null) {
br = new BufferRecycler();
if (ref == null) {
mRecyclerRef.set(new SoftReference(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 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.
*/
if (url.getHost() == null) {
return new FileInputStream(url.getPath());
}
}
return url.openStream();
}
}