package com.fasterxml.jackson.dataformat.javaprop.impl;

import java.io.*;

import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.io.IOContext;
import com.fasterxml.jackson.dataformat.javaprop.JavaPropsGenerator;
import com.fasterxml.jackson.dataformat.javaprop.io.JPropEscapes;

public class WriterBackedGenerator extends JavaPropsGenerator
{
    /*
    /**********************************************************
    /* Configuration
    /**********************************************************
     */

    
Underlying Writer used for output.
/** * Underlying {@link Writer} used for output. */
protected final Writer _out; /* /********************************************************** /* Output buffering /********************************************************** */
Intermediate buffer in which contents are buffered before being written using _out.
/** * Intermediate buffer in which contents are buffered before * being written using {@link #_out}. */
protected char[] _outputBuffer;
Pointer to the next available location in _outputBuffer
/** * Pointer to the next available location in {@link #_outputBuffer} */
protected int _outputTail = 0;
Offset to index after the last valid index in _outputBuffer. Typically same as length of the buffer.
/** * Offset to index after the last valid index in {@link #_outputBuffer}. * Typically same as length of the buffer. */
protected final int _outputEnd; /* /********************************************************** /* Life-cycle /********************************************************** */ public WriterBackedGenerator(IOContext ctxt, Writer out, int stdFeatures, ObjectCodec codec) { super(ctxt, stdFeatures, codec); _out = out; _outputBuffer = ctxt.allocConcatBuffer(); _outputEnd = _outputBuffer.length; } /* /********************************************************** /* Overridden methods, configuration /********************************************************** */ @Override public Object getOutputTarget() { return _out; } /* /********************************************************** /* Overridden methods: low-level I/O /********************************************************** */ @SuppressWarnings("deprecation") @Override public void close() throws IOException { super.close(); _flushBuffer(); _outputTail = 0; // just to ensure we don't think there's anything buffered if (_out != null) { if (_ioContext.isResourceManaged() || isEnabled(Feature.AUTO_CLOSE_TARGET)) { _out.close(); } else if (isEnabled(Feature.FLUSH_PASSED_TO_STREAM)) { // If we can't close it, we should at least flush _out.flush(); } } // Internal buffer(s) generator has can now be released as well _releaseBuffers(); } @SuppressWarnings("deprecation") @Override public void flush() throws IOException { _flushBuffer(); if (_out != null) { if (isEnabled(Feature.FLUSH_PASSED_TO_STREAM)) { _out.flush(); } } } /* /********************************************************** /* Implementations for methods from base class /********************************************************** */ @Override protected void _releaseBuffers() { char[] buf = _outputBuffer; if (buf != null) { _outputBuffer = null; _ioContext.releaseConcatBuffer(buf); } } protected void _flushBuffer() throws IOException { if (_outputTail > 0) { _out.write(_outputBuffer, 0, _outputTail); _outputTail = 0; } } @Override protected void _appendFieldName(StringBuilder path, String name) { // Note that escaping needs to be applied now... JPropEscapes.appendKey(_basePath, name); // NOTE: we do NOT yet write the key; wait until we have value; just append to path } /* /********************************************************** /* Internal methods; escaping writes /********************************************************** */ @Override protected void _writeEscapedEntry(String value) throws IOException { // note: key has been already escaped so: _writeRaw(_basePath); _writeRaw(_schema.keyValueSeparator()); _writeEscaped(value); _writeLinefeed(); } @Override protected void _writeEscapedEntry(char[] text, int offset, int len) throws IOException { // note: key has been already escaped so: _writeRaw(_basePath); _writeRaw(_schema.keyValueSeparator()); _writeEscaped(text, offset, len); _writeLinefeed(); } @Override protected void _writeUnescapedEntry(String value) throws IOException { // note: key has been already escaped so: _writeRaw(_basePath); _writeRaw(_schema.keyValueSeparator()); _writeRaw(value); _writeLinefeed(); } protected void _writeEscaped(String value) throws IOException { StringBuilder sb = JPropEscapes.appendValue(value); if (sb == null) { _writeRaw(value); } else { _writeRaw(sb); } } protected void _writeEscaped(char[] text, int offset, int len) throws IOException { _writeEscaped(new String(text, offset, len)); } protected void _writeLinefeed() throws IOException { _writeRaw(_schema.lineEnding()); } /* /********************************************************** /* Internal methods; raw writes /********************************************************** */ @Override protected void _writeRaw(char c) throws IOException { if (_outputTail >= _outputEnd) { _flushBuffer(); } _outputBuffer[_outputTail++] = c; } @Override protected void _writeRaw(String text) throws IOException { // Nothing to check, can just output as is int len = text.length(); int room = _outputEnd - _outputTail; if (room == 0) { _flushBuffer(); room = _outputEnd - _outputTail; } // But would it nicely fit in? If yes, it's easy if (room >= len) { text.getChars(0, len, _outputBuffer, _outputTail); _outputTail += len; } else { _writeRawLong(text); } } @Override protected void _writeRaw(StringBuilder text) throws IOException { // Nothing to check, can just output as is int len = text.length(); int room = _outputEnd - _outputTail; if (room == 0) { _flushBuffer(); room = _outputEnd - _outputTail; } // But would it nicely fit in? If yes, it's easy if (room >= len) { text.getChars(0, len, _outputBuffer, _outputTail); _outputTail += len; } else { _writeRawLong(text); } } @Override protected void _writeRaw(char[] text, int offset, int len) throws IOException { // Only worth buffering if it's a short write? if (len < SHORT_WRITE) { int room = _outputEnd - _outputTail; if (len > room) { _flushBuffer(); } System.arraycopy(text, offset, _outputBuffer, _outputTail, len); _outputTail += len; return; } // Otherwise, better just pass through: _flushBuffer(); _out.write(text, offset, len); } protected void _writeRawLong(String text) throws IOException { int room = _outputEnd - _outputTail; text.getChars(0, room, _outputBuffer, _outputTail); _outputTail += room; _flushBuffer(); int offset = room; int len = text.length() - room; while (len > _outputEnd) { int amount = _outputEnd; text.getChars(offset, offset+amount, _outputBuffer, 0); _outputTail = amount; _flushBuffer(); offset += amount; len -= amount; } // And last piece (at most length of buffer) text.getChars(offset, offset+len, _outputBuffer, 0); _outputTail = len; } protected void _writeRawLong(StringBuilder text) throws IOException { int room = _outputEnd - _outputTail; text.getChars(0, room, _outputBuffer, _outputTail); _outputTail += room; _flushBuffer(); int offset = room; int len = text.length() - room; while (len > _outputEnd) { int amount = _outputEnd; text.getChars(offset, offset+amount, _outputBuffer, 0); _outputTail = amount; _flushBuffer(); offset += amount; len -= amount; } // And last piece (at most length of buffer) text.getChars(offset, offset+len, _outputBuffer, 0); _outputTail = len; } }