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