001 package 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
017 import java.io.IOException;
018 import 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 */
027 public 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 }