All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.phloc.commons.io.streams.NonBlockingBufferedWriter Maven / Gradle / Ivy

There is a newer version: 5.0.0
Show newest version
/**
 * Copyright (C) 2006-2015 phloc systems
 * http://www.phloc.com
 * office[at]phloc[dot]com
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * 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 com.phloc.commons.io.streams;

import java.io.IOException;
import java.io.Writer;

import javax.annotation.Nonnegative;
import javax.annotation.Nonnull;
import javax.annotation.concurrent.NotThreadSafe;

import com.phloc.commons.CGlobal;
import com.phloc.commons.SystemProperties;
import com.phloc.commons.ValueEnforcer;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;

/**
 * This is a non-blocking version of {@link java.io.BufferedWriter}. It is 1:1
 * rip without the synchronized statements.
 * 
 * @author Philip Helger
 */
@NotThreadSafe
public class NonBlockingBufferedWriter extends Writer
{
  private static final int DEFAULT_CHAR_BUFFER_SIZE = 16 * CGlobal.BYTES_PER_KILOBYTE;

  private Writer m_aWriter;
  private char [] m_aBuf;
  private final int m_nChars;
  private int m_nNextChar;

  /**
   * Line separator string. This is the value of the line.separator property at
   * the moment that the stream was created.
   */
  private final String m_sLineSeparator;

  /**
   * Creates a buffered character-output stream that uses a default-sized output
   * buffer.
   * 
   * @param aWriter
   *        A Writer
   */
  public NonBlockingBufferedWriter (@Nonnull final Writer aWriter)
  {
    this (aWriter, DEFAULT_CHAR_BUFFER_SIZE);
  }

  /**
   * Creates a new buffered character-output stream that uses an output buffer
   * of the given size.
   * 
   * @param aWriter
   *        A Writer
   * @param nBufSize
   *        Output-buffer size, a positive integer
   * @exception IllegalArgumentException
   *            If size is <= 0
   */
  public NonBlockingBufferedWriter (@Nonnull final Writer aWriter, @Nonnegative final int nBufSize)
  {
    super (aWriter);
    ValueEnforcer.isGT0 (nBufSize, "BufSize");
    this.m_aWriter = aWriter;
    this.m_aBuf = new char [nBufSize];
    this.m_nChars = nBufSize;
    this.m_nNextChar = 0;

    this.m_sLineSeparator = SystemProperties.getLineSeparator ();
  }

  /**
   * Checks to make sure that the stream has not been closed
   * 
   * @throws IOException
   *         of the writer is not open
   */
  private void _ensureOpen () throws IOException
  {
    if (this.m_aWriter == null)
      throw new IOException ("Stream closed");
  }

  /**
   * Flushes the output buffer to the underlying character stream, without
   * flushing the stream itself. This method is non-private only so that it may
   * be invoked by PrintStream.
   * 
   * @throws IOException
   *         of the writer is not open
   */
  void flushBuffer () throws IOException
  {
    _ensureOpen ();
    if (this.m_nNextChar != 0)
    {
      this.m_aWriter.write (this.m_aBuf, 0, this.m_nNextChar);
      this.m_nNextChar = 0;
    }
  }

  /**
   * Writes a single character.
   * 
   * @exception IOException
   *            If an I/O error occurs
   */
  @Override
  public void write (final int c) throws IOException
  {
    _ensureOpen ();
    if (this.m_nNextChar >= this.m_nChars)
      flushBuffer ();
    this.m_aBuf[this.m_nNextChar++] = (char) c;
  }

  /**
   * Writes a portion of an array of characters.
   * 

* Ordinarily this method stores characters from the given array into this * stream's buffer, flushing the buffer to the underlying stream as needed. If * the requested length is at least as large as the buffer, however, then this * method will flush the buffer and write the characters directly to the * underlying stream. Thus redundant BufferedWriters will not * copy data unnecessarily. * * @param cbuf * A character array * @param nOfs * Offset from which to start reading characters * @param nLen * Number of characters to write * @exception IOException * If an I/O error occurs */ @Override @SuppressFBWarnings ("IL_INFINITE_LOOP") public void write (final char [] cbuf, final int nOfs, final int nLen) throws IOException { _ensureOpen (); ValueEnforcer.isArrayOfsLen (cbuf, nOfs, nLen); if (nLen == 0) return; if (nLen >= this.m_nChars) { /* * If the request length exceeds the size of the output buffer, flush the * buffer and then write the data directly. In this way buffered streams * will cascade harmlessly. */ flushBuffer (); this.m_aWriter.write (cbuf, nOfs, nLen); } else { int b = nOfs; final int t = nOfs + nLen; while (b < t) { final int d = Math.min (this.m_nChars - this.m_nNextChar, t - b); System.arraycopy (cbuf, b, this.m_aBuf, this.m_nNextChar, d); b += d; this.m_nNextChar += d; if (this.m_nNextChar >= this.m_nChars) flushBuffer (); } } } /** * Writes a portion of a String. *

* If the value of the len parameter is negative then no characters * are written. This is contrary to the specification of this method in the * {@linkplain java.io.Writer#write(java.lang.String,int,int) superclass}, * which requires that an {@link IndexOutOfBoundsException} be thrown. * * @param s * String to be written * @param off * Offset from which to start reading characters * @param len * Number of characters to be written * @exception IOException * If an I/O error occurs */ @Override @SuppressFBWarnings ("IL_INFINITE_LOOP") public void write (final String s, final int off, final int len) throws IOException { _ensureOpen (); int b = off; final int t = off + len; while (b < t) { final int d = Math.min (this.m_nChars - this.m_nNextChar, t - b); s.getChars (b, b + d, this.m_aBuf, this.m_nNextChar); b += d; this.m_nNextChar += d; if (this.m_nNextChar >= this.m_nChars) flushBuffer (); } } /** * Writes a line separator. The line separator string is defined by the system * property line.separator, and is not necessarily a single newline * ('\n') character. * * @exception IOException * If an I/O error occurs */ public void newLine () throws IOException { write (this.m_sLineSeparator); } /** * Flushes the stream. * * @exception IOException * If an I/O error occurs */ @Override public void flush () throws IOException { flushBuffer (); this.m_aWriter.flush (); } @Override public void close () throws IOException { if (this.m_aWriter != null) { try { flushBuffer (); } finally { this.m_aWriter.close (); this.m_aWriter = null; this.m_aBuf = null; } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy