1   /**
2    * Copyright (c) 2000-2009 Liferay, Inc. All rights reserved.
3    *
4    *
5    *
6    *
7    * The contents of this file are subject to the terms of the Liferay Enterprise
8    * Subscription License ("License"). You may not use this file except in
9    * compliance with the License. You can obtain a copy of the License by
10   * contacting Liferay, Inc. See the License for the specific language governing
11   * permissions and limitations under the License, including but not limited to
12   * distribution rights of the Software.
13   *
14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20   * SOFTWARE.
21   */
22  
23  package com.liferay.portal.kernel.io.unsync;
24  
25  import com.liferay.portal.kernel.util.CharPool;
26  import com.liferay.portal.kernel.util.StringBundler;
27  
28  import java.io.IOException;
29  import java.io.Reader;
30  
31  /**
32   * <a href="UnsyncBufferedReader.java.html"><b><i>View Source</i></b></a>
33   *
34   * <p>
35   * See http://support.liferay.com/browse/LPS-6648.
36   * </p>
37   *
38   * @author Shuyang Zhou
39   */
40  public class UnsyncBufferedReader extends Reader {
41  
42      public UnsyncBufferedReader(Reader reader) {
43          this(reader, _DEFAULT_BUFFER_SIZE);
44      }
45  
46      public UnsyncBufferedReader(Reader reader, int size) {
47          this.reader = reader;
48          buffer = new char[size];
49      }
50  
51      public void close() throws IOException {
52          if (reader != null) {
53              reader.close();
54  
55              reader = null;
56          }
57  
58          buffer = null;
59      }
60  
61      public void mark(int markLimit) throws IOException {
62          if (reader == null) {
63              throw new IOException("Reader is null");
64          }
65  
66          this.markLimit = markLimit;
67          markIndex = index;
68      }
69  
70      public boolean markSupported() {
71          return true;
72      }
73  
74      public int read() throws IOException {
75          if (reader == null) {
76              throw new IOException("Reader is null");
77          }
78  
79          if (index >= firstInvalidIndex) {
80              readUnderlyingReader();
81  
82              if (index >= firstInvalidIndex) {
83                  return -1;
84              }
85          }
86  
87          return buffer[index++];
88      }
89  
90      public int read(char[] charArray) throws IOException {
91          return read(charArray, 0, charArray.length);
92      }
93  
94      public int read(char[] charArray, int offset, int length)
95          throws IOException {
96  
97          if (reader == null) {
98              throw new IOException("Reader is null");
99          }
100 
101         if (length <= 0) {
102             return 0;
103         }
104 
105         int read = 0;
106 
107         while (true) {
108             int available = firstInvalidIndex - index;
109 
110             if ((available + read) >= length) {
111 
112                 // Enough data, stop reading
113 
114                 int leftSize = length - read;
115 
116                 System.arraycopy(buffer, index, charArray, read, leftSize);
117 
118                 index += leftSize;
119 
120                 return length;
121             }
122 
123             if (available <= 0) {
124 
125                 // No more data in buffer, continue reading
126 
127                 readUnderlyingReader();
128 
129                 available = firstInvalidIndex - index;
130 
131                 if (available <= 0) {
132 
133                     // Cannot read any more, stop reading
134 
135                     if (read == 0) {
136                         return -1;
137                     }
138                     else {
139                         return read;
140                     }
141                 }
142             }
143             else {
144 
145                 // Copy all in-memory data, continue reading
146 
147                 System.arraycopy(buffer, index, charArray, read, available);
148 
149                 index += available;
150                 read += available;
151             }
152         }
153     }
154 
155     public String readLine() throws IOException {
156         if (reader == null) {
157             throw new IOException("Reader is null");
158         }
159 
160         StringBundler sb = null;
161 
162         while (true) {
163             if (index >= firstInvalidIndex) {
164                 readUnderlyingReader();
165             }
166 
167             if (index >= firstInvalidIndex) {
168                 if ((sb != null) && (sb.index() > 0)) {
169                     return sb.toString();
170                 }
171                 else {
172                     return null;
173                 }
174             }
175 
176             boolean hasLineBreak = false;
177             char lineEndChar = 0;
178 
179             int x = index;
180             int y = index;
181 
182             while (y < firstInvalidIndex) {
183                 lineEndChar = buffer[y];
184 
185                 if ((lineEndChar == CharPool.NEW_LINE) ||
186                     (lineEndChar == CharPool.RETURN)) {
187 
188                     hasLineBreak = true;
189 
190                     break;
191                 }
192 
193                 y++;
194             }
195 
196             String line = new String(buffer, x, y - x);
197 
198             index = y;
199 
200             if (hasLineBreak) {
201                 index++;
202 
203                 if (lineEndChar == CharPool.RETURN) {
204                     if ((index < buffer.length) &&
205                         (buffer[index] == CharPool.NEW_LINE)) {
206 
207                         index++;
208                     }
209                 }
210 
211                 if (sb == null) {
212                     return line;
213                 }
214                 else {
215                     sb.append(line);
216 
217                     return sb.toString();
218                 }
219             }
220 
221             if (sb == null) {
222                 sb = new StringBundler();
223             }
224 
225             sb.append(line);
226         }
227     }
228 
229     public boolean ready() throws IOException {
230         if (reader == null) {
231             throw new IOException("Reader is null");
232         }
233 
234         return (index < firstInvalidIndex) || reader.ready();
235     }
236 
237     public void reset() throws IOException {
238         if (reader == null) {
239             throw new IOException("Reader is null");
240         }
241 
242         if (markIndex < 0) {
243             throw new IOException("Resetting to invalid mark");
244         }
245 
246         index = markIndex;
247     }
248 
249     public long skip(long skip) throws IOException {
250         if (reader == null) {
251             throw new IOException("Reader is null");
252         }
253 
254         if (skip <= 0) {
255             return 0;
256         }
257 
258         long available = firstInvalidIndex - index;
259 
260         if (available > 0) {
261 
262             // Skip the data in buffer
263 
264             if (available < skip) {
265                 skip = available;
266             }
267         }
268         else {
269 
270             // Skip the underlying reader
271 
272             if (markIndex < 0) {
273 
274                 // No mark required, skip
275 
276                 skip = reader.skip(skip);
277             }
278             else {
279 
280                 // Mark required, save the skipped data
281 
282                 readUnderlyingReader();
283 
284                 available = firstInvalidIndex - index;
285 
286                 if (available > 0) {
287 
288                     // Skip the data in buffer
289 
290                     if (available < skip) {
291                         skip = available;
292                     }
293                 }
294             }
295         }
296 
297         index += skip;
298 
299         return skip;
300     }
301 
302     protected void readUnderlyingReader() throws IOException {
303         if (markIndex < 0) {
304 
305             // No mark required, fill the buffer
306 
307             index = firstInvalidIndex = 0;
308 
309             int number = reader.read(buffer);
310 
311             if (number > 0) {
312                 firstInvalidIndex = number;
313             }
314 
315             return;
316         }
317 
318         // Mark required
319 
320         if (index >= buffer.length) {
321 
322             // Buffer is full, clean up or grow
323 
324             if ((firstInvalidIndex - markIndex) > markLimit) {
325 
326                 // Passed mark limit, get rid of all cache data
327 
328                 markIndex = -1;
329                 index = 0;
330             }
331             else if (markIndex > _MAX_MARK_WASTE_SIZE) {
332 
333                 // There are more than _MAX_MARK_WASTE_SIZE free space at the
334                 // beginning of buffer, clean up by shuffling the buffer
335 
336                 int realDataSize = index - markIndex;
337 
338                 System.arraycopy(
339                     buffer, markIndex, buffer, 0, realDataSize);
340 
341                 markIndex = 0;
342                 index = realDataSize;
343             }
344             else {
345 
346                 // Grow the buffer because we cannot get rid of cache data and
347                 // it is inefficient to shuffle the buffer
348 
349                 int newBufferSize = index << 1;
350 
351                 if ((newBufferSize - _MAX_MARK_WASTE_SIZE) > markLimit) {
352 
353                     // Make thew new buffer size larger than the mark limit
354 
355                     newBufferSize = markLimit + _MAX_MARK_WASTE_SIZE;
356                 }
357 
358                 char[] newBuffer = new char[newBufferSize];
359 
360                 System.arraycopy(buffer, 0, newBuffer, 0, index);
361 
362                 buffer = newBuffer;
363             }
364         }
365 
366         // Read underlying reader since the buffer has more space
367 
368         firstInvalidIndex = index;
369 
370         int number = reader.read(buffer, index, buffer.length - index);
371 
372         if (number > 0) {
373             firstInvalidIndex += number;
374         }
375     }
376 
377     protected char[] buffer;
378     protected int firstInvalidIndex;
379     protected int index;
380     protected int markIndex = -1;
381     protected int markLimit;
382     protected Reader reader;
383 
384     private static int _DEFAULT_BUFFER_SIZE = 8192;
385 
386     private static int _MAX_MARK_WASTE_SIZE = 4096;
387 
388 }