001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package java.io;
019:
020: import org.apache.harmony.luni.util.Msg;
021:
022: /**
023: * BufferedReader is a buffered character input reader. Buffering allows reading
024: * from character streams more efficiently. If the default size of the buffer is
025: * not practical, another size may be specified. Reading a character from a
026: * Reader class usually involves reading a character from its Stream or
027: * subsequent Reader. It is advisable to wrap a BufferedReader around those
028: * Readers whose read operations may have high latency. For example, the
029: * following code
030: *
031: * <pre>
032: * BufferedReader inReader = new BufferedReader(new FileReader("file.java"));
033: * </pre>
034: *
035: * will buffer input for the file <code>file.java</code>.
036: *
037: * @see BufferedWriter
038: * @since 1.1
039: */
040: public class BufferedReader extends Reader {
041:
042: private Reader in;
043:
044: private char[] buf;
045:
046: private int marklimit = -1;
047:
048: private int count;
049:
050: private int markpos = -1;
051:
052: private int pos;
053:
054: /**
055: * Constructs a new BufferedReader on the Reader <code>in</code>. The
056: * default buffer size (8K) is allocated and all reads can now be filtered
057: * through this BufferedReader.
058: *
059: * @param in
060: * the Reader to buffer reads on.
061: */
062: public BufferedReader(Reader in) {
063: super (in);
064: this .in = in;
065: buf = new char[8192];
066: }
067:
068: /**
069: * Constructs a new BufferedReader on the Reader <code>in</code>. The
070: * buffer size is specified by the parameter <code>size</code> and all
071: * reads can now be filtered through this BufferedReader.
072: *
073: * @param in
074: * the Reader to buffer reads on.
075: * @param size
076: * the size of buffer to allocate.
077: * @throws IllegalArgumentException
078: * if the size is <= 0
079: */
080: public BufferedReader(Reader in, int size) {
081: super (in);
082: if (size <= 0) {
083: throw new IllegalArgumentException(Msg.getString("K0058")); //$NON-NLS-1$
084: }
085: this .in = in;
086: buf = new char[size];
087: }
088:
089: /**
090: * Close the Reader. This implementation closes the Reader being filtered
091: * and releases the buffer used by this reader. If this BufferedReader has
092: * already been closed, nothing is done.
093: *
094: * @throws IOException
095: * If an error occurs attempting to close this BufferedReader.
096: */
097: @Override
098: public void close() throws IOException {
099: synchronized (lock) {
100: if (!isClosed()) {
101: in.close();
102: buf = null;
103: }
104: }
105: }
106:
107: private int fillbuf() throws IOException {
108: if (markpos == -1 || (pos - markpos >= marklimit)) {
109: /* Mark position not set or exceeded readlimit */
110: int result = in.read(buf, 0, buf.length);
111: if (result > 0) {
112: markpos = -1;
113: pos = 0;
114: count = result == -1 ? 0 : result;
115: }
116: return result;
117: }
118: if (markpos == 0 && marklimit > buf.length) {
119: /* Increase buffer size to accommodate the readlimit */
120: int newLength = buf.length * 2;
121: if (newLength > marklimit) {
122: newLength = marklimit;
123: }
124: char[] newbuf = new char[newLength];
125: System.arraycopy(buf, 0, newbuf, 0, buf.length);
126: buf = newbuf;
127: } else if (markpos > 0) {
128: System
129: .arraycopy(buf, markpos, buf, 0, buf.length
130: - markpos);
131: }
132:
133: /* Set the new position and mark position */
134: pos -= markpos;
135: count = markpos = 0;
136: int charsread = in.read(buf, pos, buf.length - pos);
137: count = charsread == -1 ? pos : pos + charsread;
138: return charsread;
139: }
140:
141: /**
142: * Answer a boolean indicating whether or not this BufferedReader is closed.
143: *
144: * @return <code>true</code> if this reader is closed, <code>false</code>
145: * otherwise
146: */
147: private boolean isClosed() {
148: return buf == null;
149: }
150:
151: /**
152: * Set a Mark position in this BufferedReader. The parameter
153: * <code>readLimit</code> indicates how many characters can be read before
154: * a mark is invalidated. Sending reset() will reposition the reader back to
155: * the marked position provided <code>readLimit</code> has not been
156: * surpassed.
157: *
158: * @param readlimit
159: * an int representing how many characters must be read before
160: * invalidating the mark.
161: *
162: * @throws IOException
163: * If an error occurs attempting mark this BufferedReader.
164: * @throws IllegalArgumentException
165: * If readlimit is < 0
166: */
167: @Override
168: public void mark(int readlimit) throws IOException {
169: if (readlimit < 0) {
170: throw new IllegalArgumentException();
171: }
172: synchronized (lock) {
173: if (isClosed()) {
174: throw new IOException(Msg.getString("K005b")); //$NON-NLS-1$
175: }
176: marklimit = readlimit;
177: markpos = pos;
178: }
179: }
180:
181: /**
182: * Answers a boolean indicating whether or not this Reader supports mark()
183: * and reset(). This implementation answers <code>true</code>.
184: *
185: * @return <code>true</code> if mark() and reset() are supported,
186: * <code>false</code> otherwise
187: */
188: @Override
189: public boolean markSupported() {
190: return true;
191: }
192:
193: /**
194: * Reads a single character from this reader and returns the result as an
195: * int. The 2 higher-order characters are set to 0. If the end of reader was
196: * encountered then return -1. This implementation either returns a
197: * character from the buffer or if there are no characters available, fill
198: * the buffer then return a character or -1.
199: *
200: * @return the character read or -1 if end of reader.
201: *
202: * @throws IOException
203: * If the BufferedReader is already closed or some other IO
204: * error occurs.
205: */
206: @Override
207: public int read() throws IOException {
208: synchronized (lock) {
209: if (isClosed()) {
210: throw new IOException(Msg.getString("K005b")); //$NON-NLS-1$
211: }
212: /* Are there buffered characters available? */
213: if (pos < count || fillbuf() != -1) {
214: return buf[pos++];
215: }
216: return -1;
217: }
218: }
219:
220: /**
221: * Reads at most <code>length</code> characters from this BufferedReader
222: * and stores them at <code>offset</code> in the character array
223: * <code>buffer</code>. Returns the number of characters actually read or
224: * -1 if the end of reader was encountered. If all the buffered characters
225: * have been used, a mark has not been set, and the requested number of
226: * characters is larger than this Readers buffer size, this implementation
227: * bypasses the buffer and simply places the results directly into
228: * <code>buffer</code>.
229: *
230: * @param buffer
231: * character array to store the read characters
232: * @param offset
233: * offset in buf to store the read characters
234: * @param length
235: * maximum number of characters to read
236: * @return number of characters read or -1 if end of reader.
237: *
238: * @throws IOException
239: * If the BufferedReader is already closed or some other IO
240: * error occurs.
241: */
242: @Override
243: public int read(char[] buffer, int offset, int length)
244: throws IOException {
245: synchronized (lock) {
246: if (isClosed()) {
247: throw new IOException(Msg.getString("K005b")); //$NON-NLS-1$
248: }
249: if (offset < 0 || offset > buffer.length - length
250: || length < 0) {
251: throw new IndexOutOfBoundsException();
252: }
253: if (length == 0) {
254: return 0;
255: }
256: int required;
257: if (pos < count) {
258: /* There are bytes available in the buffer. */
259: int copylength = count - pos >= length ? length : count
260: - pos;
261: System.arraycopy(buf, pos, buffer, offset, copylength);
262: pos += copylength;
263: if (copylength == length || !in.ready()) {
264: return copylength;
265: }
266: offset += copylength;
267: required = length - copylength;
268: } else {
269: required = length;
270: }
271:
272: while (true) {
273: int read;
274: /*
275: * If we're not marked and the required size is greater than the
276: * buffer, simply read the bytes directly bypassing the buffer.
277: */
278: if (markpos == -1 && required >= buf.length) {
279: read = in.read(buffer, offset, required);
280: if (read == -1) {
281: return required == length ? -1 : length
282: - required;
283: }
284: } else {
285: if (fillbuf() == -1) {
286: return required == length ? -1 : length
287: - required;
288: }
289: read = count - pos >= required ? required : count
290: - pos;
291: System.arraycopy(buf, pos, buffer, offset, read);
292: pos += read;
293: }
294: required -= read;
295: if (required == 0) {
296: return length;
297: }
298: if (!in.ready()) {
299: return length - required;
300: }
301: offset += read;
302: }
303: }
304: }
305:
306: /**
307: * Answers a <code>String</code> representing the next line of text
308: * available in this BufferedReader. A line is represented by 0 or more
309: * characters followed by <code>'\n'</code>, <code>'\r'</code>,
310: * <code>'\r\n'</code> or end of stream. The <code>String</code> does not
311: * include the newline sequence.
312: * In EBCDIC systems, a new line can also be represented by the
313: * <code>'\u0085'</code> (NEL) character.
314: *
315: * @return the contents of the line or null if no characters were read
316: * before end of stream.
317: *
318: * @throws IOException
319: * If the BufferedReader is already closed or some other IO
320: * error occurs.
321: */
322: public String readLine() throws IOException {
323: synchronized (lock) {
324: if (isClosed()) {
325: throw new IOException(Msg.getString("K005b")); //$NON-NLS-1$
326: }
327: /* Are there buffered characters available? */
328: if ((pos >= count) && (fillbuf() == -1)) {
329: return null;
330: }
331: for (int charPos = pos; charPos < count; charPos++) {
332: char ch = buf[charPos];
333: if ((ch > '\r') && (ch != '\u0085')) {
334: continue;
335: }
336: if (ch == '\n') {
337: String res = new String(buf, pos, charPos - pos);
338: pos = charPos + 1;
339: return res;
340: } else if (ch == '\r') {
341: String res = new String(buf, pos, charPos - pos);
342: pos = charPos + 1;
343: if (((pos < count) || (fillbuf() != -1))
344: && (buf[pos] == '\n')) {
345: pos++;
346: }
347: return res;
348: } else if (ch == '\u0085') {
349: /* Also handle the EBCDIC NEL character */
350: String res = new String(buf, pos, charPos - pos);
351: pos = charPos + 1;
352: return res;
353: }
354: }
355:
356: char eol = '\0';
357: StringBuilder result = new StringBuilder(80);
358: /* Typical Line Length */
359:
360: result.append(buf, pos, count - pos);
361: pos = count;
362: while (true) {
363: /* Are there buffered characters available? */
364: if (pos >= count) {
365: if (eol == '\n') {
366: return result.toString();
367: }
368: // attempt to fill buffer
369: if (fillbuf() == -1) {
370: // characters or null.
371: return result.length() > 0 || eol != '\0' ? result
372: .toString()
373: : null;
374: }
375: }
376: for (int charPos = pos; charPos < count; charPos++) {
377: if (eol == '\0') {
378: if ((buf[charPos] == '\n' || buf[charPos] == '\r')
379: || (buf[charPos] == '\u0085')) {
380: eol = buf[charPos];
381: }
382: } else if (eol == '\r' && (buf[charPos] == '\n')) {
383: if (charPos > pos) {
384: result.append(buf, pos, charPos - pos - 1);
385: }
386: pos = charPos + 1;
387: return result.toString();
388: } else if (eol != '\0') {
389: if (charPos > pos) {
390: result.append(buf, pos, charPos - pos - 1);
391: }
392: pos = charPos;
393: return result.toString();
394: }
395: }
396: if (eol == '\0') {
397: result.append(buf, pos, count - pos);
398: } else {
399: result.append(buf, pos, count - pos - 1);
400: }
401: pos = count;
402: }
403: }
404:
405: }
406:
407: /**
408: * Answers a <code>boolean</code> indicating whether or not this Reader is
409: * ready to be read without blocking. If the result is <code>true</code>,
410: * the next <code>read()</code> will not block. If the result is
411: * <code>false</code> this Reader may or may not block when
412: * <code>read()</code> is sent.
413: *
414: * @return <code>true</code> if the receiver will not block when
415: * <code>read()</code> is called, <code>false</code> if unknown
416: * or blocking will occur.
417: *
418: * @throws IOException
419: * If the BufferedReader is already closed or some other IO
420: * error occurs.
421: */
422: @Override
423: public boolean ready() throws IOException {
424: synchronized (lock) {
425: if (isClosed()) {
426: throw new IOException(Msg.getString("K005b")); //$NON-NLS-1$
427: }
428: return ((count - pos) > 0) || in.ready();
429: }
430: }
431:
432: /**
433: * Reset this BufferedReader's position to the last <code>mark()</code>
434: * location. Invocations of <code>read()/skip()</code> will occur from
435: * this new location. If this Reader was not marked, throw IOException.
436: *
437: * @throws IOException
438: * If a problem occurred, the receiver does not support
439: * <code>mark()/reset()</code>, or no mark has been set.
440: */
441: @Override
442: public void reset() throws IOException {
443: synchronized (lock) {
444: if (isClosed()) {
445: throw new IOException(Msg.getString("K005b")); //$NON-NLS-1$
446: }
447: if (markpos == -1) {
448: throw new IOException(Msg.getString("K005c")); //$NON-NLS-1$
449: }
450: pos = markpos;
451: }
452: }
453:
454: /**
455: * Skips <code>amount</code> number of characters in this Reader.
456: * Subsequent <code>read()</code>'s will not return these characters
457: * unless <code>reset()</code> is used. Skipping characters may invalidate
458: * a mark if marklimit is surpassed.
459: *
460: * @param amount
461: * the maximum number of characters to skip.
462: * @return the number of characters actually skipped.
463: *
464: * @throws IOException
465: * If the BufferedReader is already closed or some other IO
466: * error occurs.
467: * @throws IllegalArgumentException
468: * If amount is negative
469: */
470: @Override
471: public long skip(long amount) throws IOException {
472: if (amount < 0) {
473: throw new IllegalArgumentException();
474: }
475: synchronized (lock) {
476: if (isClosed()) {
477: throw new IOException(Msg.getString("K005b")); //$NON-NLS-1$
478: }
479: if (amount < 1) {
480: return 0;
481: }
482: if (count - pos >= amount) {
483: pos += amount;
484: return amount;
485: }
486:
487: long read = count - pos;
488: pos = count;
489: while (read < amount) {
490: if (fillbuf() == -1) {
491: return read;
492: }
493: if (count - pos >= amount - read) {
494: pos += amount - read;
495: return amount;
496: }
497: // Couldn't get all the characters, skip what we read
498: read += (count - pos);
499: pos = count;
500: }
501: return amount;
502: }
503: }
504: }
|