/* * Copyright (C) 2003-2011 eXo Platform SAS. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.etk.common.io.unsync; import java.io.IOException; import java.io.Reader; import org.etk.common.utils.CharPool; import org.etk.common.utils.StringBundler; /** * Created by The eXo Platform SAS Author : eXoPlatform * thanhvucong.78@google.com Aug 4, 2011 */ public class UnsyncBufferedReader extends Reader { public UnsyncBufferedReader(Reader reader) { this(reader, _DEFAULT_BUFFER_SIZE); } public UnsyncBufferedReader(Reader reader, int size) { this.reader = reader; buffer = new char[size]; } public void close() throws IOException { if (reader != null) { reader.close(); reader = null; } buffer = null; } public void mark(int markLimit) throws IOException { if (reader == null) { throw new IOException("Reader is null"); } this.markLimit = markLimit; markIndex = index; } public boolean markSupported() { return true; } public int read() throws IOException { if (reader == null) { throw new IOException("Reader is null"); } if (index >= firstInvalidIndex) { readUnderlyingReader(); if (index >= firstInvalidIndex) { return -1; } } return buffer[index++]; } public int read(char[] charArray) throws IOException { return read(charArray, 0, charArray.length); } public int read(char[] charArray, int offset, int length) throws IOException { if (reader == null) { throw new IOException("Reader is null"); } if (length <= 0) { return 0; } int read = 0; while (true) { int available = firstInvalidIndex - index; if ((available + read) >= length) { // Enough data, stop reading int leftSize = length - read; System.arraycopy(buffer, index, charArray, read, leftSize); index += leftSize; return length; } if (available <= 0) { // No more data in buffer, continue reading readUnderlyingReader(); available = firstInvalidIndex - index; if (available <= 0) { // Cannot read any more, stop reading if (read == 0) { return -1; } else { return read; } } } else { // Copy all in-memory data, continue reading System.arraycopy(buffer, index, charArray, read, available); index += available; read += available; } } } public String readLine() throws IOException { if (reader == null) { throw new IOException("Reader is null"); } StringBundler sb = null; while (true) { if (index >= firstInvalidIndex) { readUnderlyingReader(); } if (index >= firstInvalidIndex) { if ((sb != null) && (sb.index() > 0)) { return sb.toString(); } else { return null; } } boolean hasLineBreak = false; char lineEndChar = 0; int x = index; int y = index; while (y < firstInvalidIndex) { lineEndChar = buffer[y]; if ((lineEndChar == CharPool.NEW_LINE) || (lineEndChar == CharPool.RETURN)) { hasLineBreak = true; break; } y++; } String line = new String(buffer, x, y - x); index = y; if (hasLineBreak) { index++; if (lineEndChar == CharPool.RETURN) { if ((index < buffer.length) && (buffer[index] == CharPool.NEW_LINE)) { index++; } } if (sb == null) { return line; } else { sb.append(line); return sb.toString(); } } if (sb == null) { sb = new StringBundler(); } sb.append(line); } } public boolean ready() throws IOException { if (reader == null) { throw new IOException("Reader is null"); } return (index < firstInvalidIndex) || reader.ready(); } public void reset() throws IOException { if (reader == null) { throw new IOException("Reader is null"); } if (markIndex < 0) { throw new IOException("Resetting to invalid mark"); } index = markIndex; } public long skip(long skip) throws IOException { if (reader == null) { throw new IOException("Reader is null"); } if (skip <= 0) { return 0; } long available = firstInvalidIndex - index; if (available > 0) { // Skip the data in buffer if (available < skip) { skip = available; } } else { // Skip the underlying reader if (markIndex < 0) { // No mark required, skip skip = reader.skip(skip); } else { // Mark required, save the skipped data readUnderlyingReader(); available = firstInvalidIndex - index; if (available > 0) { // Skip the data in buffer if (available < skip) { skip = available; } } } } index += skip; return skip; } protected void readUnderlyingReader() throws IOException { if (markIndex < 0) { // No mark required, fill the buffer index = firstInvalidIndex = 0; int number = reader.read(buffer); if (number > 0) { firstInvalidIndex = number; } return; } // Mark required if (index >= buffer.length) { // Buffer is full, clean up or grow if ((firstInvalidIndex - markIndex) > markLimit) { // Passed mark limit, get rid of all cache data markIndex = -1; index = 0; } else if (markIndex > _MAX_MARK_WASTE_SIZE) { // There are more than _MAX_MARK_WASTE_SIZE free space at the // beginning of buffer, clean up by shuffling the buffer int realDataSize = index - markIndex; System.arraycopy(buffer, markIndex, buffer, 0, realDataSize); markIndex = 0; index = realDataSize; } else { // Grow the buffer because we cannot get rid of cache data and // it is inefficient to shuffle the buffer int newBufferSize = index << 1; if ((newBufferSize - _MAX_MARK_WASTE_SIZE) > markLimit) { // Make thew new buffer size larger than the mark limit newBufferSize = markLimit + _MAX_MARK_WASTE_SIZE; } char[] newBuffer = new char[newBufferSize]; System.arraycopy(buffer, 0, newBuffer, 0, index); buffer = newBuffer; } } // Read underlying reader since the buffer has more space firstInvalidIndex = index; int number = reader.read(buffer, index, buffer.length - index); if (number > 0) { firstInvalidIndex += number; } } protected char[] buffer; protected int firstInvalidIndex; protected int index; protected int markIndex = -1; protected int markLimit; protected Reader reader; private static int _DEFAULT_BUFFER_SIZE = 8192; private static int _MAX_MARK_WASTE_SIZE = 4096; }