JavaPropsFactory.java
package tools.jackson.dataformat.javaprop;
import java.io.*;
import java.util.*;
import tools.jackson.core.*;
import tools.jackson.core.base.TextualTSFactory;
import tools.jackson.core.exc.StreamReadException;
import tools.jackson.core.io.IOContext;
import tools.jackson.dataformat.javaprop.impl.PropertiesBackedGenerator;
import tools.jackson.dataformat.javaprop.impl.WriterBackedGenerator;
import tools.jackson.dataformat.javaprop.io.Latin1Reader;
@SuppressWarnings("resource")
public class JavaPropsFactory
extends TextualTSFactory
implements java.io.Serializable
{
private static final long serialVersionUID = 1L;
public final static String FORMAT_NAME_JAVA_PROPERTIES = "java_properties";
protected final static String CHARSET_ID_LATIN1 = "ISO-8859-1";
final static JavaPropsSchema EMPTY_SCHEMA;
static {
EMPTY_SCHEMA = JavaPropsSchema.emptySchema();
}
/*
/**********************************************************************
/* Factory construction, configuration
/**********************************************************************
*/
public JavaPropsFactory() {
// No format-specific features yet so:
super(StreamReadConstraints.defaults(), StreamWriteConstraints.defaults(),
ErrorReportConfiguration.defaults(),
0, 0);
}
protected JavaPropsFactory(JavaPropsFactory src)
{
super(src);
}
/**
* Constructors used by {@link JavaPropsFactoryBuilder} for instantiation.
*
* @since 3.0
*/
protected JavaPropsFactory(JavaPropsFactoryBuilder b)
{
super(b);
}
@Override
public JavaPropsFactoryBuilder rebuild() {
return new JavaPropsFactoryBuilder(this);
}
/**
* Main factory method to use for constructing {@link JavaPropsFactory} instances with
* different configuration.
*/
public static JavaPropsFactoryBuilder builder() {
return new JavaPropsFactoryBuilder();
}
@Override
public JavaPropsFactory copy() {
return new JavaPropsFactory(this);
}
/**
* Instances are immutable so just return `this`
*/
@Override
public TokenStreamFactory snapshot() {
return this;
}
/*
/**********************************************************************
/* Introspection
/**********************************************************************
*/
@Override
public Version version() {
return PackageVersion.VERSION;
}
// Not positional
@Override
public boolean requiresPropertyOrdering() {
return false;
}
// Not using char[] internally
@Override
public boolean canUseCharArrays() { return false; }
@Override
public boolean canParseAsync() {
// 30-Sep-2017, tatu: No async parsing yet
return false;
}
/*
/**********************************************************************
/* Format support
/**********************************************************************
*/
@Override
public String getFormatName() {
return FORMAT_NAME_JAVA_PROPERTIES;
}
@Override
public boolean canUseSchema(FormatSchema schema) {
return schema instanceof JavaPropsSchema;
}
// No format-specific configuration, yet:
@Override
public Class<? extends FormatFeature> getFormatReadFeatureType() {
return null;
}
@Override
public Class<? extends FormatFeature> getFormatWriteFeatureType() {
return null;
}
@Override
public int getFormatReadFeatures() { return 0; }
@Override
public int getFormatWriteFeatures() { return 0; }
/*
/**********************************************************************
/* Extended parser/generator factory methods
/**********************************************************************
*/
/**
* Convenience method to allow feeding a pre-parsed {@link Properties}
* (or, generally {@link java.util.Map}) instance as input.
*/
public JavaPropsParser createParser(ObjectReadContext readCtxt,
JavaPropsSchema schema, Map<?,?> content) {
return new JavaPropsParser(readCtxt,
_createContext(_createContentReference(content), true),
readCtxt.getStreamReadFeatures(_streamReadFeatures),
schema, content, content);
}
/**
* Convenience method to allow using a pre-constructed {@link Map}
* instance as output target, so that serialized property values
* are added.
*/
public JavaPropsGenerator createGenerator(ObjectWriteContext writeCtxt,
JavaPropsSchema schema, Map<?,?> target)
{
if (schema == null) {
schema = EMPTY_SCHEMA;
}
return new PropertiesBackedGenerator(writeCtxt,
_createContext(_createContentReference(target), true),
writeCtxt.getStreamWriteFeatures(_streamWriteFeatures),
schema, target);
}
/*
/**********************************************************************
/* Overridden internal factory methods, parser
/**********************************************************************
*/
/* // fine as-is:
@Override
protected IOContext _createContext(Object srcRef, boolean resourceManaged) {
return super._createContext(srcRef, resourceManaged);
}
*/
@Override
protected JsonParser _createParser(ObjectReadContext readCtxt, IOContext ioCtxt,
InputStream in)
{
Properties props = _loadProperties(in, ioCtxt);
return new JavaPropsParser(readCtxt, ioCtxt,
readCtxt.getStreamReadFeatures(_streamReadFeatures),
_getSchema(readCtxt),
in, props);
}
@Override
protected JsonParser _createParser(ObjectReadContext readCtxt, IOContext ioCtxt,
Reader r) {
Properties props = _loadProperties(r, ioCtxt);
return new JavaPropsParser(readCtxt, ioCtxt,
readCtxt.getStreamReadFeatures(_streamReadFeatures),
_getSchema(readCtxt),
r, props);
}
@Override
protected JsonParser _createParser(ObjectReadContext readCtxt, IOContext ioCtxt,
char[] data, int offset, int len,
boolean recyclable)
{
return _createParser(readCtxt, ioCtxt, new CharArrayReader(data, offset, len));
}
@Override
protected JsonParser _createParser(ObjectReadContext readCtxt, IOContext ioCtxt,
byte[] data, int offset, int len)
{
return _createParser(readCtxt, ioCtxt, new Latin1Reader(data, offset, len));
}
@Override
protected JsonParser _createParser(ObjectReadContext readCtxt, IOContext ctxt,
DataInput input) {
return _unsupported();
}
private final JavaPropsSchema _getSchema(ObjectReadContext readCtxt) {
FormatSchema sch = readCtxt.getSchema();
if (sch == null) {
return JavaPropsParser.DEFAULT_SCHEMA;
}
return (JavaPropsSchema) sch;
}
/*
/**********************************************************************
/* Overridden internal factory methods, generator
/**********************************************************************
*/
@Override
protected JsonGenerator _createGenerator(ObjectWriteContext writeCtxt,
IOContext ioCtxt, Writer out)
{
return new WriterBackedGenerator(writeCtxt, ioCtxt,
writeCtxt.getStreamWriteFeatures(_streamWriteFeatures),
_getSchema(writeCtxt),
out);
}
@Override
protected JsonGenerator _createUTF8Generator(ObjectWriteContext writeCtxt,
IOContext ioCtxt, OutputStream out)
{
return new WriterBackedGenerator(writeCtxt, ioCtxt,
writeCtxt.getStreamWriteFeatures(_streamWriteFeatures),
_getSchema(writeCtxt),
_createWriter(ioCtxt, out, null));
}
@Override
protected Writer _createWriter(IOContext ctioCtxtxt, OutputStream out, JsonEncoding enc)
{
// 27-Jan-2016, tatu: Properties javadoc is quite clear on Latin-1 (ISO-8859-1) being
// the default, so let's actually override
try {
return new OutputStreamWriter(out, CHARSET_ID_LATIN1);
} catch (IOException e) {
throw _wrapIOFailure(e);
}
}
private final JavaPropsSchema _getSchema(ObjectWriteContext ctxt) {
FormatSchema sch = ctxt.getSchema();
if (sch == null) {
return JavaPropsParser.DEFAULT_SCHEMA;
}
return (JavaPropsSchema) sch;
}
/*
/**********************************************************************
/* Low-level methods for reading/writing Properties; currently
/* we simply delegate to `java.util.Properties`
/**********************************************************************
*/
protected Properties _loadProperties(InputStream in, IOContext ctxt)
{
// NOTE: Properties default to ISO-8859-1 (aka Latin-1), NOT UTF-8; this
// as per JDK documentation
return _loadProperties(new Latin1Reader(ctxt, in), ctxt);
}
protected Properties _loadProperties(Reader r0, IOContext ctxt)
{
Properties props = new Properties();
// May or may not want to close the reader, so...
try {
if (ctxt.isResourceManaged() || isEnabled(StreamReadFeature.AUTO_CLOSE_SOURCE)) {
try (Reader r = r0) {
props.load(r);
}
} else {
props.load(r0);
}
} catch (IllegalArgumentException e) {
_reportReadException("Invalid content, problem: "+e.getMessage(), e);
} catch (IOException e) {
throw _wrapIOFailure(e);
}
return props;
}
protected <T> T _reportReadException(String msg, Exception rootCause)
{
throw new StreamReadException((JsonParser) null, msg, rootCause);
}
}