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: /**
021: * LineNumberReader is a buffered character input reader which counts line
022: * numbers as data is being read. The line number starts at 0 and is incremented
023: * any time '\r', '\n', or '\r\n' is read.
024: *
025: * @see BufferedWriter
026: */
027: public class LineNumberReader extends BufferedReader {
028:
029: private int lineNumber;
030:
031: private int markedLineNumber = -1;
032:
033: private boolean lastWasCR;
034:
035: private boolean markedLastWasCR;
036:
037: /**
038: * Constructs a new buffered LineNumberReader on the Reader <code>in</code>.
039: * The default buffer size (8K) is allocated and all reads can now be
040: * filtered through this LineNumberReader.
041: *
042: * @param in
043: * the Reader to buffer reads on.
044: */
045: public LineNumberReader(Reader in) {
046: super (in);
047: }
048:
049: /**
050: * Constructs a new buffered LineNumberReader on the Reader <code>in</code>.
051: * The buffer size is specified by the parameter <code>size</code> and all
052: * reads can now be filtered through this LineNumberReader.
053: *
054: * @param in
055: * the Reader to buffer reads on.
056: * @param size
057: * the size of buffer to allocate.
058: */
059: public LineNumberReader(Reader in, int size) {
060: super (in, size);
061: }
062:
063: /**
064: * Answers a int representing the current line number for this
065: * LineNumberReader.
066: *
067: * @return int the current line number.
068: */
069: public int getLineNumber() {
070: synchronized (lock) {
071: return lineNumber;
072: }
073: }
074:
075: /**
076: * Set a Mark position in this LineNumberReader. The parameter
077: * <code>readLimit</code> indicates how many characters can be read before
078: * a mark is invalidated. Sending reset() will reposition the reader back to
079: * the marked position provided <code>readLimit</code> has not been
080: * surpassed. The lineNumber associated with this marked position will also
081: * be saved and restored when reset() is sent provided
082: * <code>readLimit</code> has not been surpassed.
083: *
084: * @param readlimit
085: * an int representing how many characters must be read before
086: * invalidating the mark.
087: *
088: * @throws IOException
089: * If an error occurs attempting mark this LineNumberReader.
090: */
091: @Override
092: public void mark(int readlimit) throws IOException {
093: synchronized (lock) {
094: super .mark(readlimit);
095: markedLineNumber = lineNumber;
096: markedLastWasCR = lastWasCR;
097: }
098: }
099:
100: /**
101: * Reads a single char from this LineNumberReader and returns the result as
102: * an int. The low-order 2 bytes are returned or -1 of the end of reader was
103: * encountered. This implementation returns a char from the target reader.
104: * The line number count is incremented if a line terminator is encountered.
105: * A line delimiter sequence is determined by '\r', '\n', or '\r\n'. In this
106: * method, the sequence is always translated into '\n'.
107: *
108: * @return int The char read or -1 if end of reader.
109: *
110: * @throws IOException
111: * If the reader is already closed or another IOException
112: * occurs.
113: */
114: @SuppressWarnings("fallthrough")
115: @Override
116: public int read() throws IOException {
117: synchronized (lock) {
118: int ch = super .read();
119: if (ch == '\n' && lastWasCR) {
120: ch = super .read();
121: }
122: lastWasCR = false;
123: switch (ch) {
124: case '\r':
125: ch = '\n';
126: lastWasCR = true;
127: // fall through
128: case '\n':
129: lineNumber++;
130: }
131: return ch;
132: }
133: }
134:
135: /**
136: * Reads at most <code>count</code> chars from this LineNumberReader and
137: * stores them in char array <code>buffer</code> starting at offset
138: * <code>offset</code>. Answer the number of chars actually read or -1 if
139: * no chars were read and end of reader was encountered. This implementation
140: * reads chars from the target stream. The line number count is incremented
141: * if a line terminator is encountered. A line delimiter sequence is
142: * determined by '\r', '\n', or '\r\n'. In this method, the sequence is
143: * always translated into '\n'.
144: *
145: * @param buffer
146: * the char array in which to store the read chars.
147: * @param offset
148: * the offset in <code>buffer</code> to store the read chars.
149: * @param count
150: * the maximum number of chars to store in <code>buffer</code>.
151: * @return the number of chars actually read or -1 if end of reader.
152: *
153: * @throws IOException
154: * If the reader is already closed or another IOException
155: * occurs.
156: */
157:
158: @Override
159: public int read(char[] buffer, int offset, int count)
160: throws IOException {
161: synchronized (lock) {
162: int read = super .read(buffer, offset, count);
163: if (read == -1) {
164: return -1;
165: }
166: for (int i = 0; i < read; i++) {
167: char ch = buffer[offset + i];
168: if (ch == '\r') {
169: lineNumber++;
170: lastWasCR = true;
171: } else if (ch == '\n') {
172: if (!lastWasCR) {
173: lineNumber++;
174: }
175: lastWasCR = false;
176: } else {
177: lastWasCR = false;
178: }
179: }
180: return read;
181: }
182: }
183:
184: /**
185: * Answers a <code>String</code> representing the next line of text
186: * available in this LineNumberReader. A line is represented by 0 or more
187: * characters followed by <code>'\n'</code>, <code>'\r'</code>,
188: * <code>"\n\r"</code> or end of stream. The <code>String</code> does
189: * not include the newline sequence.
190: *
191: * @return String the contents of the line or null if no characters were
192: * read before end of stream.
193: *
194: * @throws IOException
195: * If the LineNumberReader is already closed or some other IO
196: * error occurs.
197: */
198: @Override
199: public String readLine() throws IOException {
200: synchronized (lock) {
201: lineNumber++;
202: return super .readLine();
203: }
204: }
205:
206: /**
207: * Reset this LineNumberReader to the last marked location. If the
208: * <code>readlimit</code> has been passed or no <code>mark</code> has
209: * been set, throw IOException. This implementation resets the target
210: * reader. It also resets the line count to what is was when this reader was
211: * marked.
212: *
213: * @throws IOException
214: * If the reader is already closed or another IOException
215: * occurs.
216: */
217: @Override
218: public void reset() throws IOException {
219: synchronized (lock) {
220: super .reset();
221: lineNumber = markedLineNumber;
222: lastWasCR = markedLastWasCR;
223: }
224: }
225:
226: /**
227: * Sets the lineNumber of this LineNumberReader to the specified
228: * <code>lineNumber</code>. Note that this may have side effects on the
229: * line number associated with the last marked position.
230: *
231: * @param lineNumber
232: * the new lineNumber value.
233: */
234: public void setLineNumber(int lineNumber) {
235: synchronized (lock) {
236: this .lineNumber = lineNumber;
237: }
238: }
239:
240: /**
241: * Skips <code>count</code> number of chars in this LineNumberReader.
242: * Subsequent <code>read()</code>'s will not return these chars unless
243: * <code>reset()</code> is used. This implementation skips
244: * <code>count</code> number of chars in the target stream and increments
245: * the lineNumber count as chars are skipped.
246: *
247: * @param count
248: * the number of chars to skip.
249: * @return the number of chars actually skipped.
250: *
251: * @throws IOException
252: * If the reader is already closed or another IOException
253: * occurs.
254: */
255: @Override
256: public long skip(long count) throws IOException {
257: if (count < 0) {
258: throw new IllegalArgumentException();
259: }
260: synchronized (lock) {
261: for (int i = 0; i < count; i++) {
262: if (read() == -1) {
263: return i;
264: }
265: }
266: return count;
267: }
268: }
269: }
|