1 package net.sf.logdistiller.util;
2
3 /*
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 import java.io.IOException;
18 import java.io.Reader;
19
20 /**
21 * A reader reading its content from another reader, but buffering the data to be able to give it back on demand. When
22 * data is given back, it is not maintained in memory any more. When using this class, take care to get data regularly,
23 * or all the data read will stay in memory, risking OutOfMemoryException !
24 *
25 * @see #freeData
26 */
27 public class BufferingReader
28 extends Reader
29 {
30 private int bufferBlockSize;
31
32 private char[] buffer;
33
34 private int begin = 0;
35
36 private int end = 0;
37
38 private final Reader reader;
39
40 public BufferingReader( Reader reader, int bufferBlockSize )
41 {
42 this.reader = reader;
43 this.bufferBlockSize = bufferBlockSize;
44 buffer = new char[bufferBlockSize];
45 }
46
47 public BufferingReader( Reader reader )
48 {
49 this( reader, 16384 );
50 }
51
52 public boolean ready()
53 throws IOException
54 {
55 return reader.ready();
56 }
57
58 public void close()
59 throws IOException
60 {
61 reader.close();
62 }
63
64 public int read( char[] cbuf, int off, int len )
65 throws IOException
66 {
67 int count = reader.read( cbuf, off, len );
68 if ( count > 0 )
69 {
70 if ( end + count >= buffer.length )
71 {
72 prepareBuffer( count + ( end - begin ) );
73 }
74 System.arraycopy( cbuf, off, buffer, end, count );
75 end += count;
76 }
77 return count;
78 }
79
80 /**
81 * Get the length of data available in the buffer.
82 *
83 * @return available data length
84 */
85 public int getDataLength()
86 {
87 return ( end - begin );
88 }
89
90 /**
91 * Get some data that have been buffered, and free corresponding space in the buffer.
92 *
93 * @param count the number of characters to get
94 * @return the corresponding data
95 * @throws IndexOutOfBoundsException if the amount of data asked is more than what is available in the buffer
96 * @see #getDataLength()
97 */
98 public String freeData( int count )
99 {
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 }