/*
* Copyright (C) 2016 Stephen Ostermiller
* http://ostermiller.org/contact.pl?regarding=Java+Utilities
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.

*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* See COPYING.TXT for details.
*/

import java.io.*;
import java.util.ArrayList;

/**
* A reader which reads sequentially from multiple sources.
* More information about this class is available from <a target="_top" href=
* "http://ostermiller.org/utils/">ostermiller.org</a>.
*
* @author Stephen Ostermiller http://ostermiller.org/contact.pl?regarding=Java+Utilities
* @since ostermillerutils 1.04.00
*/
public class ConcatReader extends Reader {

/**
   * Current index to readerQueue
   *
   * @since ostermillerutils 1.04.01
   */
  private int readerQueueIndex = 0;

/**
   * Queue of readers that have yet to be read from.
   *
   * @since ostermillerutils 1.04.01
   */
  private ArrayList<Reader> readerQueue = new ArrayList<Reader>();

/**
   * A cache of the current reader from the readerQueue
   * to avoid unneeded access to the queue which must
   * be synchronized.
   *
   * @since ostermillerutils 1.04.01
   */
  private Reader currentReader = null;

/**
   * true iff the client may add more readers.
   *
   * @since ostermillerutils 1.04.01
   */
  private boolean doneAddingReaders = false;

/**
   * Causes the addReader method to throw IllegalStateException
   * and read() methods to return -1 (end of stream)
   * when there is no more available data.
   * <p>
   * Calling this method when this class is no longer accepting
   * more readers has no effect.
   *
   * @since ostermillerutils 1.04.01
   */
  public void lastReaderAdded(){
    doneAddingReaders = true;
  }

/**
   * Add the given reader to the queue of readers from which to
   * concatenate data.
   *
   * @param in Reader to add to the concatenation.
   * @throws IllegalStateException if more readers can't be added because lastReaderAdded() has been called, close() has been called, or a constructor with reader parameters was used.
   *
   * @since ostermillerutils 1.04.01
   */
  public void addReader(Reader in){
    synchronized(readerQueue){
      if (in == null) throw new NullPointerException();
      if (closed) throw new IllegalStateException("ConcatReader has been closed");
      if (doneAddingReaders) throw new IllegalStateException("Cannot add more readers - the last reader has already been added.");
      readerQueue.add(in);
    }
  }

/**
   * Add the given reader to the queue of readers from which to
   * concatenate data.
   *
   * @param in Reader to add to the concatenation.
   * @throws IllegalStateException if more readers can't be added because lastReaderAdded() has been called, close() has been called, or a constructor with reader parameters was used.
   * @throws NullPointerException the array of readers, or any of the contents is null.
   *
   * @since ostermillerutils 1.04.01
   */
  public void addReaders(Reader[] in){
    for (Reader element: in) {
      addReader(element);
    }
  }

/**
   * Gets the current reader, looking at the next
   * one in the list if the current one is null.
   *
   * @since ostermillerutils 1.04.01
   */
  private Reader getCurrentReader(){
    if (currentReader == null && readerQueueIndex < readerQueue.size()){
      synchronized(readerQueue){
        // reader queue index is advanced only by the nextReader()
        // method.  Don't do it here.
        currentReader = readerQueue.get(readerQueueIndex);
      }
    }
    return currentReader;
  }

/**
   * Indicate that we are done with the current reader and we should
   * advance to the next reader.
   *
   * @since ostermillerutils 1.04.01
   */
  private void advanceToNextReader(){
    currentReader = null;
    readerQueueIndex++;
  }

/**
   * True iff this the close() method has been called on this stream.
   *
   * @since ostermillerutils 1.04.00
   */
  private boolean closed = false;

/**
   * Create a new reader that can dynamically accept new sources.
   * <p>
   * New sources should be added using the addReader() method.
   * When all sources have been added the lastReaderAdded() should
   * be called so that read methods can return -1 (end of stream).
   * <p>
   * Adding new sources may by interleaved with read calls.
   *
   * @since ostermillerutils 1.04.01
   */
  public ConcatReader(){
    // Empty Constructor
  }

/**
   * Create a new reader with one source.
   * <p>
   * When using this constructor, more readers cannot
   * be added later, and calling addReader() will
   * throw an illegal state Exception.
   *
   * @param in reader to use as a source.
   *
   * @throws NullPointerException if in is null
   *
   * @since ostermillerutils 1.04.00
   */
  public ConcatReader(Reader in){
    addReader(in);
    lastReaderAdded();
  }

/**
   * Create a new reader with two sources.
   * <p>
   * When using this constructor, more readers cannot
   * be added later, and calling addReader() will
   * throw an illegal state Exception.
   *
   * @param in1 first reader to use as a source.
   * @param in2 second reader to use as a source.
   *
   * @throws NullPointerException if either source is null.
   *
   * @since ostermillerutils 1.04.00
   */
  public ConcatReader(Reader in1, Reader in2){
    addReader(in1);
    addReader(in2);
    lastReaderAdded();
  }

/**
   * Create a new reader with an arbitrary number of sources.
   * <p>
   * When using this constructor, more readers cannot
   * be added later, and calling addReader() will
   * throw an illegal state Exception.
   *
   * @param in readers to use as a sources.
   *
   * @throws NullPointerException if the input array on any element is null.
   *
   * @since ostermillerutils 1.04.00
   */
  public ConcatReader(Reader[] in){
    addReaders(in);
    lastReaderAdded();
  }

/**
   * Read a single character. This method will block until a
   * character is available, an I/O error occurs, or the end of all underlying
   * streams are reached.
   * <p>
   * If this class in not done accepting readers and the end of the last known
   * stream is reached, this method will block forever unless another thread
   * adds a reader or interrupts.
   *
   * @return The character read, as an integer in the range 0 to 65535 (0x00-0xffff),
   *    or -1 if the end of the stream has been reached
   *
   * @throws IOException - If an I/O error occurs
   *
   * @since ostermillerutils 1.04.00
   */
  @Override public int read() throws IOException {
    if (closed) throw new IOException("Reader closed");
    int r = -1;
    while (r == -1){
      Reader in = getCurrentReader();
      if (in == null){
        if (doneAddingReaders) return -1;
        try {
          Thread.sleep(100);
        } catch (InterruptedException iox){
          throw new IOException("Interrupted");
        }
      } else {
        r = in.read();
        if (r == -1) advanceToNextReader();
      }
    }
    return r;
  }

/**
   * Read characters into an array. This method will block until some input is available, an
   * I/O error occurs, or the end of all underlying
   * streams are reached.
   * <p>
   * If this class in not done accepting readers and the end of the last known
   * stream is reached, this method will block forever unless another thread
   * adds a reader or interrupts.
   *
   * @param cbuf - Destination buffer
   * @return The number of characters read, or -1 if the end of the stream has been reached
   *
   * @throws IOException - If an I/O error occurs
   * @throws NullPointerException - If the buffer is null.
   *
   * @since ostermillerutils 1.04.00
   */
  @Override public int read(char[] cbuf) throws IOException {
    return read(cbuf, 0, cbuf.length);
  }

/**
   * Read characters into a portion of an array. This method will block until
   * some input is available, an I/O error occurs, or the end of all underlying
   * streams are reached.
   * <p>
   * If this class in not done accepting readers and the end of the last known
   * stream is reached, this method will block forever unless another thread
   * adds a reader or interrupts.
   *
   * @param cbuf Destination buffer
   * @param off Offset at which to start storing characters
   * @param len Maximum number of characters to read
   * @return The number of characters read, or -1 if the end of the stream has been reached
   *
   * @throws IOException - If an I/O error occurs
   * @throws NullPointerException - If the buffer is null.
   * @throws IndexOutOfBoundsException - if length or offset are not possible.
   *
   * @since ostermillerutils 1.04.00
   */
  @Override public int read(char[] cbuf, int off, int len) throws IOException {
    if (off < 0 || len < 0 || off + len > cbuf.length) throw new IndexOutOfBoundsException();
    if (closed) throw new IOException("Reader closed");
    int r = -1;
    while (r == -1){
      Reader in = getCurrentReader();
      if (in == null){
        if (doneAddingReaders) return -1;
        try {
          Thread.sleep(100);
        } catch (InterruptedException iox){
          throw new IOException("Interrupted");
        }
      } else {
        r = in.read(cbuf, off, len);
        if (r == -1) advanceToNextReader();
      }
    }
    return r;
  }

/**
   * Skip characters. This method will block until some characters are
   * available, an I/O error occurs, or the end of the stream is reached.
   * <p>
   * If this class in not done accepting readers and the end of the last known
   * stream is reached, this method will block forever unless another thread
   * adds a reader or interrupts.
   *
   * @param n the number of characters to skip
   * @return The number of characters actually skipped
   *
   * @throws IllegalArgumentException If n is negative.
   * @throws IOException If an I/O error occurs
   *
   * @since ostermillerutils 1.04.00
   */
  @Override public long skip(long n) throws IOException {
    if (closed) throw new IOException("Reader closed");
    if (n <= 0) return 0;
    long s = -1;
    while (s <= 0){
      Reader in = getCurrentReader();
      if (in == null){
        if (doneAddingReaders) return 0;
        try {
          Thread.sleep(100);
        } catch (InterruptedException iox){
          throw new IOException("Interrupted");
        }
      } else {
        s = in.skip(n);
        // When nothing was skipped it is a bit of a puzzle.
        // The most common cause is that the end of the underlying
        // stream was reached.  In which case calling skip on it
        // will always return zero.  If somebody were calling skip
        // until it skipped everything they needed, there would
        // be an infinite loop if we were to return zero here.
        // If we get zero, let us try to read one character so
        // we can see if we are at the end of the stream.  If so,
        // we will move to the next.
        if (s <= 0) {
          // read() will advance to the next stream for us, so don't do it again
          s = ((read()==-1)?-1:1);
        }
      }

}
    return s;
  }

/**
   * Tell whether this stream is ready to be read.
   *
   * @return True if the next read() is guaranteed not to block for input,
   *    false otherwise. Note that returning false does not guarantee that the next
   *    read will block.
   *
   * @throws IOException If an I/O error occurs
   *
   * @since ostermillerutils 1.04.00
   */
  @Override public boolean ready() throws IOException {
    if (closed) throw new IOException("Reader closed");
    Reader in = getCurrentReader();
    if (in == null) return false;
    return in.ready();
  }

/**
   * Close the stream and any underlying streams.
   * Once a stream has been closed, further read(), ready(), mark(), or reset()
   * invocations will throw an IOException. Closing a previously-closed stream,
   * however, has no effect.
   *
   * @throws IOException If an I/O error occurs
   *
   * @since ostermillerutils 1.04.00
   */
  @Override public void close() throws IOException {
    if (closed) return;
    for (Reader reader: readerQueue) {
      reader.close();
    }
    closed = true;
  }

/**
   * Mark not supported.
   *
   * @throws IOException because mark is not supported.
   *
   * @since ostermillerutils 1.04.00
   */
  @Override public void mark(int readlimit) throws IOException {
    throw new IOException("Mark not supported");
  }

/**
   * Reset not supported.
   *
   * @throws IOException because reset is not supported.
   *
   * @since ostermillerutils 1.04.00
   */
  @Override public void reset() throws IOException {
    throw new IOException("Reset not supported");
  }

/**
   * Mark not supported.
   *
   * @return false
   *
   * @since ostermillerutils 1.04.00
   */
  @Override public boolean markSupported(){
    return false;
  }
}

Reads sequentially from multiple sources的更多相关文章

  1. jdk1.7升级到jdk1.8后出错: [ERROR] javadoc: warning - Multiple sources of package comments found for package

    from: http://blog.joda.org/2014/02/turning-off-doclint-in-jdk-8-javadoc.html [ERROR] javadoc: warnin ...

  2. Sphinx 2.2.11-release reference manual

    1. Introduction 1.1. About 1.2. Sphinx features 1.3. Where to get Sphinx 1.4. License 1.5. Credits 1 ...

  3. Flume NG Getting Started(Flume NG 新手入门指南)

    Flume NG Getting Started(Flume NG 新手入门指南)翻译 新手入门 Flume NG是什么? 有什么改变? 获得Flume NG 从源码构建 配置 flume-ng全局选 ...

  4. TopHat

    What is TopHat? TopHat is a program that aligns RNA-Seq reads to a genome in order to identify exon- ...

  5. 【转】 svn 错误 以及 中文翻译

    直接Ctrl+F 搜索你要找的错 # # Simplified Chinese translation for subversion package # This file is distribute ...

  6. RAC的QA

    RAC: Frequently Asked Questions [ID 220970.1]   修改时间 13-JAN-2011     类型 FAQ     状态 PUBLISHED   Appli ...

  7. GATK-BWA-MEM handle GRCh38 alternate contig mappings

    1. For the Impatient # Download bwakit (or from <http://sourceforge.net/projects/bio-bwa/files/bw ...

  8. [转] KVM Internals, code and more

    KVM Kernel-based Virtual Machine Internals, code and more http://slides.com/braoru/kvm#/ What behind ...

  9. worker启动executor源码分析-executor.clj

    在"supervisor启动worker源码分析-worker.clj"一文中,我们详细讲解了worker是如何初始化的.主要通过调用mk-worker函数实现的.在启动worke ...

随机推荐

  1. 尝试用Uplodify

    尝试用Uplodify     Uplodify官方 前台index代码: @{ Layout = null; } <script src="~/Scripts/jquery-1.8. ...

  2. Entity Framework 学习之--Ling to entity实现分页

    最近用MVC做的一个项目涉及到分页,中间用了entity framework来查数据库,不用直接写sql语句,方便了很多. 一般分页的思路是获得两个变量的值: 1.一共有多少条记录 totalCoun ...

  3. Function模式 -- 深入理解javascript

    /* 一.回调函数 函数A作为另外一个函数B的参数 */ var menuId = $("ul.nav").first().attr("id"); var re ...

  4. Java 7 中 NIO.2 的使用——文件递归操作

    众所周知,递归编程是一项有争议的技术,因为它需要大量的内存,但是它能简化一些编程任务.基本上,一个递归操作都是程序调用自己传递参数修改的值或者参数传递到当前的程序循环中.递归编程通常用来计算阶乘斐波那 ...

  5. Java 7 中 NIO.2 的使用——第四节 文件和目录

    Files类提供了很多方法用于检查在于你真正实际去操作一个文件或目录.这些方法强烈推荐,也非常有用,也能避免很多异常的发生.例如,一个很好的习惯就是在你试着移动一个文件从一个地方到另一个地方的时候,先 ...

  6. BZOJ3874 codevs3361 宅男计划

    AC通道1:http://www.lydsy.com/JudgeOnline/problem.php?id=3874 AC通道2:http://codevs.cn/problem/3361/ [题目分 ...

  7. BZOJ2752: [HAOI2012]高速公路(road)

    2752: [HAOI2012]高速公路(road) Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 608  Solved: 199[Submit][ ...

  8. Matlab求范数

    对 p = 2,这称为弗罗贝尼乌斯范数(Frobenius norm)或希尔伯特-施密特范数( Hilbert–Schmidt norm),不过后面这个术语通常只用于希尔伯特空间.这个范数可用不同的方 ...

  9. zip&rar格式压缩包的解压缩

    实际工作中例子: 首先需要引入两个jar包: 注意:支持压缩软件4.20及以上版本 (1)压缩包的解压: (2)压缩包的压缩打包 zip格式:

  10. mongo二维数组操作

    有2个嵌套的数组: 如果我想查询comments里score大于5的记录: testProvider.find({"comments.score":{"$gt" ...