JavaPropsGenerator.java
package tools.jackson.dataformat.javaprop;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import tools.jackson.core.*;
import tools.jackson.core.base.GeneratorBase;
import tools.jackson.core.io.IOContext;
import tools.jackson.core.util.JacksonFeatureSet;
import tools.jackson.dataformat.javaprop.io.JPropWriteContext;
import tools.jackson.dataformat.javaprop.util.Markers;
public abstract class JavaPropsGenerator
extends GeneratorBase
{
// As an optimization we try coalescing short writes into
// buffer; but pass longer directly.
protected final static int SHORT_WRITE = 100;
/*
/**********************************************************************
/* Configuration
/**********************************************************************
*/
/**
* Definition of mapping of logically structured property names into actual
* flattened property names.
*/
protected final JavaPropsSchema _schema;
/*
/**********************************************************************
/* Output state
/**********************************************************************
*/
/**
* Current context, in form we can use it (GeneratorBase has
* untyped reference; left as null)
*/
protected JPropWriteContext _streamWriteContext;
/*
/**********************************************************************
/* Output buffering
/**********************************************************************
*/
protected final StringBuilder _basePath = new StringBuilder(50);
protected boolean _headerChecked;
protected int _indentLength;
/*
/**********************************************************************
/* Life-cycle
/**********************************************************************
*/
public JavaPropsGenerator(ObjectWriteContext writeCtxt, IOContext ioCtxt,
int stdFeatures, JavaPropsSchema schema)
{
super(writeCtxt, ioCtxt, stdFeatures);
_streamWriteContext = JPropWriteContext.createRootContext();
_schema = schema;
// Indentation to use?
if (_streamWriteContext.inRoot()) {
String indent = _schema.lineIndentation();
_indentLength = (indent == null) ? 0 : indent.length();
if (_indentLength > 0) {
_basePath.setLength(0);
_basePath.append(indent);
_streamWriteContext = JPropWriteContext.createRootContext(_indentLength);
}
// [dataformats-text#100]: Allow use of optional prefix
final String prefix = _schema.prefix();
if (prefix != null) {
_basePath.append(prefix);
}
}
}
/*
/**********************************************************************
/* Versioned
/**********************************************************************
*/
@Override
public Version version() {
return PackageVersion.VERSION;
}
/*
/**********************************************************************
/* Overridden output state handling methods
/**********************************************************************
*/
@Override
public TokenStreamContext streamWriteContext() {
return _streamWriteContext;
}
@Override
public Object currentValue() {
return _streamWriteContext.currentValue();
}
@Override
public void assignCurrentValue(Object v) {
_streamWriteContext.assignCurrentValue(v);
}
/*
/**********************************************************************
/* Overridden methods, configuration
/**********************************************************************
*/
// public abstract getOutputTarget()
// Base impl fine
/*
@Override
public int getOutputBuffered() {
return -1;
}
*/
@Override
public PrettyPrinter getPrettyPrinter() {
return null;
}
@Override
public FormatSchema getSchema() { return _schema; }
/*
/**********************************************************************
/* Overrides: capability introspection methods
/**********************************************************************
*/
@Override
public boolean canWriteObjectId() { return false; }
@Override
public boolean canWriteTypeId() { return false; }
@Override
public boolean canOmitProperties() { return true; }
@Override
public JacksonFeatureSet<StreamWriteCapability> streamWriteCapabilities() {
return DEFAULT_TEXTUAL_WRITE_CAPABILITIES;
}
/*
/**********************************************************************
/* Overridden methods; writing property names
/**********************************************************************
*/
@Override
public JsonGenerator writeName(String name) throws JacksonException
{
if (!_streamWriteContext.writeName(name)) {
_reportError("Cannot write a property name, expecting a value");
}
// also, may need to output header if this would be first write
if (!_headerChecked) {
_headerChecked = true;
String header = _schema.header();
if (header != null && !header.isEmpty()) {
_writeRaw(header);
}
}
// Ok; append to base path at this point.
// First: ensure possibly preceding property name is removed:
_streamWriteContext.truncatePath(_basePath);
if (_basePath.length() > _indentLength) {
String sep = _schema.pathSeparator();
if (!sep.isEmpty()) {
_basePath.append(sep);
}
}
_appendPropertyName(_basePath, name);
return this;
}
@Override
public JsonGenerator writePropertyId(long id) throws JacksonException {
// 15-Aug-2019, tatu: should be improved to avoid String generation
return writeName(Long.toString(id));
}
protected abstract void _appendPropertyName(StringBuilder path, String name);
/*
/**********************************************************************
/* Public API: structural output
/**********************************************************************
*/
@Override
public JsonGenerator writeStartArray() throws JacksonException {
_verifyValueWrite("start an array");
_streamWriteContext = _streamWriteContext.createChildArrayContext(null,
_basePath.length());
streamWriteConstraints().validateNestingDepth(_streamWriteContext.getNestingDepth());
return this;
}
@Override
public JsonGenerator writeStartArray(Object currValue) throws JacksonException {
_verifyValueWrite("start an array");
_streamWriteContext = _streamWriteContext.createChildArrayContext(currValue,
_basePath.length());
streamWriteConstraints().validateNestingDepth(_streamWriteContext.getNestingDepth());
return this;
}
@Override
public JsonGenerator writeEndArray() throws JacksonException {
if (!_streamWriteContext.inArray()) {
_reportError("Current context not an Array but "+_streamWriteContext.typeDesc());
}
_streamWriteContext = _streamWriteContext.getParent();
return this;
}
@Override
public JsonGenerator writeStartObject() throws JacksonException {
_verifyValueWrite("start an object");
_streamWriteContext = _streamWriteContext.createChildObjectContext(null, _basePath.length());
streamWriteConstraints().validateNestingDepth(_streamWriteContext.getNestingDepth());
return this;
}
@Override
public JsonGenerator writeStartObject(Object forValue) throws JacksonException {
_verifyValueWrite("start an object");
_streamWriteContext = _streamWriteContext.createChildObjectContext(forValue, _basePath.length());
streamWriteConstraints().validateNestingDepth(_streamWriteContext.getNestingDepth());
return this;
}
@Override
public JsonGenerator writeEndObject() throws JacksonException
{
if (!_streamWriteContext.inObject()) {
_reportError("Current context not an Ibject but "+_streamWriteContext.typeDesc());
}
_streamWriteContext = _streamWriteContext.getParent();
return this;
}
/*
/**********************************************************************
/* Output method implementations, textual
/**********************************************************************
*/
@Override
public JsonGenerator writeString(String text) throws JacksonException
{
if (text == null) {
return writeNull();
}
_verifyValueWrite("write String value");
_writeEscapedEntry(text);
return this;
}
@Override
public JsonGenerator writeString(char[] text, int offset, int len)
throws JacksonException
{
_verifyValueWrite("write String value");
_writeEscapedEntry(text, offset, len);
return this;
}
@Override
public JsonGenerator writeRawUTF8String(byte[] text, int offset, int len)throws JacksonException
{
return _reportUnsupportedOperation();
}
@Override
public JsonGenerator writeUTF8String(byte[] text, int offset, int len) throws JacksonException
{
return writeString(new String(text, offset, len, StandardCharsets.UTF_8));
}
/*
/**********************************************************************
/* Output method implementations, unprocessed ("raw")
/**********************************************************************
*/
@Override
public JsonGenerator writeRaw(String text) throws JacksonException {
_writeRaw(text);
return this;
}
@Override
public JsonGenerator writeRaw(String text, int offset, int len) throws JacksonException {
_writeRaw(text.substring(offset, offset+len));
return this;
}
@Override
public JsonGenerator writeRaw(char[] text, int offset, int len) throws JacksonException {
_writeRaw(text, offset, len);
return this;
}
@Override
public JsonGenerator writeRaw(char c) throws JacksonException {
_writeRaw(c);
return this;
}
@Override
public JsonGenerator writeRaw(SerializableString text) throws JacksonException {
writeRaw(text.toString());
return this;
}
/*
/**********************************************************************
/* Output method implementations, base64-encoded binary
/**********************************************************************
*/
@Override
public JsonGenerator writeBinary(Base64Variant b64variant, byte[] data, int offset, int len)
throws JacksonException
{
if (data == null) {
return writeNull();
}
_verifyValueWrite("write Binary value");
// ok, better just Base64 encode as a String...
if (offset > 0 || (offset+len) != data.length) {
data = Arrays.copyOfRange(data, offset, offset+len);
}
String encoded = b64variant.encode(data);
_writeEscapedEntry(encoded);
return this;
}
/*
/**********************************************************************
/* Output method implementations, scalars
/**********************************************************************
*/
@Override
public JsonGenerator writeBoolean(boolean state) throws JacksonException
{
_verifyValueWrite("write boolean value");
_writeUnescapedEntry(state ? "true" : "false");
return this;
}
@Override
public JsonGenerator writeNumber(short v) throws JacksonException {
return writeNumber((int) v);
}
@Override
public JsonGenerator writeNumber(int i) throws JacksonException
{
_verifyValueWrite("write number");
_writeUnescapedEntry(String.valueOf(i));
return this;
}
@Override
public JsonGenerator writeNumber(long l) throws JacksonException
{
_verifyValueWrite("write number");
_writeUnescapedEntry(String.valueOf(l));
return this;
}
@Override
public JsonGenerator writeNumber(BigInteger v) throws JacksonException
{
if (v == null) {
return writeNull();
}
_verifyValueWrite("write number");
_writeUnescapedEntry(String.valueOf(v));
return this;
}
@Override
public JsonGenerator writeNumber(double d) throws JacksonException
{
_verifyValueWrite("write number");
_writeUnescapedEntry(String.valueOf(d));
return this;
}
@Override
public JsonGenerator writeNumber(float f) throws JacksonException
{
_verifyValueWrite("write number");
_writeUnescapedEntry(String.valueOf(f));
return this;
}
@Override
public JsonGenerator writeNumber(BigDecimal dec) throws JacksonException
{
if (dec == null) {
return writeNull();
}
_verifyValueWrite("write number");
String str = isEnabled(StreamWriteFeature.WRITE_BIGDECIMAL_AS_PLAIN) ? dec.toPlainString() : dec.toString();
_writeUnescapedEntry(str);
return this;
}
@Override
public JsonGenerator writeNumber(String encodedValue) throws JacksonException
{
if (encodedValue == null) {
return writeNull();
}
_verifyValueWrite("write number");
_writeUnescapedEntry(encodedValue);
return this;
}
@Override
public JsonGenerator writeNull() throws JacksonException
{
_verifyValueWrite("write null value");
_writeUnescapedEntry("");
return this;
}
/*
/**********************************************************************
/* Implementations for methods from base class
/**********************************************************************
*/
// protected void _releaseBuffers()
// protected void _flushBuffer() throws JacksonException
@Override
protected void _verifyValueWrite(String typeMsg) throws JacksonException
{
// first, check that name/value cadence works
if (!_streamWriteContext.writeValue()) {
_reportError("Cannot "+typeMsg+", expecting a property name");
}
// and if so, update path if we are in array
if (_streamWriteContext.inArray()) {
// remove possible path remnants from an earlier sibling
_streamWriteContext.truncatePath(_basePath);
int ix = _streamWriteContext.getCurrentIndex() + _schema.firstArrayOffset();
if (_schema.writeIndexUsingMarkers()) {
Markers m = _schema.indexMarker();
// no leading path separator, if using enclosed indexes
_basePath.append(m.getStart());
_basePath.append(ix);
_basePath.append(m.getEnd());
} else {
// leading path separator, if using "simple" index markers
if (_basePath.length() > 0) {
String sep = _schema.pathSeparator();
if (!sep.isEmpty()) {
_basePath.append(sep);
}
}
_basePath.append(ix);
}
}
}
/*
/**********************************************************************
/* Abstract methods for sub-classes
/**********************************************************************
*/
protected abstract void _writeEscapedEntry(String value) throws JacksonException;
protected abstract void _writeEscapedEntry(char[] text, int offset, int len) throws JacksonException;
protected abstract void _writeUnescapedEntry(String value) throws JacksonException;
protected abstract void _writeRaw(char c) throws JacksonException;
protected abstract void _writeRaw(String text) throws JacksonException;
protected abstract void _writeRaw(StringBuilder text) throws JacksonException;
protected abstract void _writeRaw(char[] text, int offset, int len) throws JacksonException;
}