001package net.sf.logdistiller.util; 002 003/* 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017import java.io.IOException; 018import java.io.Reader; 019 020/** 021 * A reader reading its content from another reader, but buffering the data to be able to give it back on demand. When 022 * data is given back, it is not maintained in memory any more. When using this class, take care to get data regularly, 023 * or all the data read will stay in memory, risking OutOfMemoryException ! 024 * 025 * @see #freeData 026 */ 027public class BufferingReader 028 extends Reader 029{ 030 private int bufferBlockSize; 031 032 private char[] buffer; 033 034 private int begin = 0; 035 036 private int end = 0; 037 038 private final Reader reader; 039 040 public BufferingReader( Reader reader, int bufferBlockSize ) 041 { 042 this.reader = reader; 043 this.bufferBlockSize = bufferBlockSize; 044 buffer = new char[bufferBlockSize]; 045 } 046 047 public BufferingReader( Reader reader ) 048 { 049 this( reader, 16384 ); 050 } 051 052 public boolean ready() 053 throws IOException 054 { 055 return reader.ready(); 056 } 057 058 public void close() 059 throws IOException 060 { 061 reader.close(); 062 } 063 064 public int read( char[] cbuf, int off, int len ) 065 throws IOException 066 { 067 int count = reader.read( cbuf, off, len ); 068 if ( count > 0 ) 069 { 070 if ( end + count >= buffer.length ) 071 { 072 prepareBuffer( count + ( end - begin ) ); 073 } 074 System.arraycopy( cbuf, off, buffer, end, count ); 075 end += count; 076 } 077 return count; 078 } 079 080 /** 081 * Get the length of data available in the buffer. 082 * 083 * @return available data length 084 */ 085 public int getDataLength() 086 { 087 return ( end - begin ); 088 } 089 090 /** 091 * Get some data that have been buffered, and free corresponding space in the buffer. 092 * 093 * @param count the number of characters to get 094 * @return the corresponding data 095 * @throws IndexOutOfBoundsException if the amount of data asked is more than what is available in the buffer 096 * @see #getDataLength() 097 */ 098 public String freeData( int count ) 099 { 100 if ( count > getDataLength() ) 101 { 102 throw new IndexOutOfBoundsException( "asked for " + count + " chars, but buffer contains only " 103 + getDataLength() + " chars." ); 104 } 105 String data = new String( buffer, begin, count ); 106 begin += count; 107 return data; 108 } 109 110 /** 111 * Get the size of the buffer. 112 * 113 * @return the buffer size 114 */ 115 public int getBufferSize() 116 { 117 return buffer.length; 118 } 119 120 /** 121 * Shrink the buffer to its minimal size to store actual data. In normal use, the buffer grows as needed but never 122 * shrinks. 123 */ 124 public void shrinkBuffer() 125 { 126 int size = calculateBufferSize( end - begin ); 127 if ( size < buffer.length ) 128 { 129 char[] oldBuffer = buffer; 130 buffer = new char[size]; 131 end -= begin; 132 System.arraycopy( oldBuffer, begin, buffer, 0, end ); 133 begin = 0; 134 } 135 } 136 137 /** 138 * prepare the internal buffer to insert some data. Existing data in the buffer are moved to the beginning of the 139 * buffer (so that begin = 0), and a new buffer is reallocated if its length was not sufficient. 140 * 141 * @param size the minimal size of the buffer 142 */ 143 private void prepareBuffer( int size ) 144 { 145 char[] oldBuffer = buffer; 146 if ( size > buffer.length ) 147 { 148 // buffer is not big enough: must enlarge (to a multiple of bufferBlockSize) 149 buffer = new char[calculateBufferSize( size )]; 150 } 151 end -= begin; 152 System.arraycopy( oldBuffer, begin, buffer, 0, end ); 153 begin = 0; 154 } 155 156 private int calculateBufferSize( int size ) 157 { 158 return bufferBlockSize * ( size / bufferBlockSize + 1 ); 159 } 160}