View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.csv;
19  
20  import java.io.BufferedReader;
21  import java.io.IOException;
22  import java.io.Reader;
23  
24  /**
25   * ExtendedBufferedReader
26   *
27   * A special reader decorator which supports more
28   * sophisticated access to the underlying reader object.
29   *
30   * In particular the reader supports a look-ahead option,
31   * which allows you to see the next char returned by
32   * next().
33   */
34  class ExtendedBufferedReader extends BufferedReader {
35  
36      /** The end of stream symbol */
37      static final int END_OF_STREAM = -1;
38  
39      /** Undefined state for the lookahead char */
40      static final int UNDEFINED = -2;
41  
42      /** The lookahead chars */
43      private int lookaheadChar = UNDEFINED;
44  
45      /** The last char returned */
46      private int lastChar = UNDEFINED;
47  
48      /** The line counter */
49      private int lineCounter = 0;
50  
51      private CharBuffer line = new CharBuffer();
52  
53      /**
54       * Created extended buffered reader using default buffer-size
55       */
56      ExtendedBufferedReader(Reader r) {
57          super(r);
58          /* note uh: do not fetch the first char here,
59          *          because this might block the method!
60          */
61      }
62  
63      /**
64       * Reads the next char from the input stream.
65       *
66       * @return the next char or END_OF_STREAM if end of stream has been reached.
67       */
68      @Override
69      public int read() throws IOException {
70          // initialize the lookahead
71          if (lookaheadChar == UNDEFINED) {
72              lookaheadChar = super.read();
73          }
74          lastChar = lookaheadChar;
75          if (super.ready()) {
76              lookaheadChar = super.read();
77          } else {
78              lookaheadChar = UNDEFINED;
79          }
80          if (lastChar == '\n') {
81              lineCounter++;
82          }
83          return lastChar;
84      }
85  
86      /**
87       * Returns the last read character again.
88       *
89       * @return the last read char or UNDEFINED
90       */
91      int readAgain() {
92          return lastChar;
93      }
94  
95      /**
96       * Non-blocking reading of len chars into buffer buf starting
97       * at bufferposition off.
98       * <p/>
99       * performs an iterative read on the underlying stream
100      * as long as the following conditions hold:
101      * - less than len chars have been read
102      * - end of stream has not been reached
103      * - next read is not blocking
104      *
105      * @return nof chars actually read or END_OF_STREAM
106      */
107     @Override
108     public int read(char[] buf, int off, int len) throws IOException {
109         // do not claim if len == 0
110         if (len == 0) {
111             return 0;
112         }
113 
114         // init lookahead, but do not block !!
115         if (lookaheadChar == UNDEFINED) {
116             if (ready()) {
117                 lookaheadChar = super.read();
118             } else {
119                 return -1;
120             }
121         }
122         // 'first read of underlying stream'
123         if (lookaheadChar == -1) {
124             return -1;
125         }
126         // continue until the lookaheadChar would block
127         int cOff = off;
128         while (len > 0 && ready()) {
129             if (lookaheadChar == -1) {
130                 // eof stream reached, do not continue
131                 return cOff - off;
132             } else {
133                 buf[cOff++] = (char) lookaheadChar;
134                 if (lookaheadChar == '\n') {
135                     lineCounter++;
136                 }
137                 lastChar = lookaheadChar;
138                 lookaheadChar = super.read();
139                 len--;
140             }
141         }
142         return cOff - off;
143     }
144 
145     /**
146      * @return A String containing the contents of the line, not
147      *         including any line-termination characters, or null
148      *         if the end of the stream has been reached
149      */
150     @Override
151     public String readLine() throws IOException {
152 
153         if (lookaheadChar == UNDEFINED) {
154             lookaheadChar = super.read();
155         }
156 
157         line.clear(); //reuse
158 
159         // return null if end of stream has been reached
160         if (lookaheadChar == END_OF_STREAM) {
161             return null;
162         }
163         // do we have a line termination already
164         char laChar = (char) lookaheadChar;
165         if (laChar == '\n' || laChar == '\r') {
166             lastChar = lookaheadChar;
167             lookaheadChar = super.read();
168             // ignore '\r\n' as well
169             if ((char) lookaheadChar == '\n') {
170                 lastChar = lookaheadChar;
171                 lookaheadChar = super.read();
172             }
173             lineCounter++;
174             return line.toString();
175         }
176 
177         // create the rest-of-line return and update the lookahead
178         line.append(laChar);
179         String restOfLine = super.readLine(); // TODO involves copying
180         lastChar = lookaheadChar;
181         lookaheadChar = super.read();
182         if (restOfLine != null) {
183             line.append(restOfLine);
184         }
185         lineCounter++;
186         return line.toString();
187     }
188 
189     /**
190      * Unsupported
191      */
192     @Override
193     public long skip(long n) throws IllegalArgumentException, IOException {
194         throw new UnsupportedOperationException("CSV has no reason to implement this");
195     }
196 
197     /**
198      * Returns the next char in the stream without consuming it.
199      *
200      * Remember the next char read by read(..) will always be
201      * identical to lookAhead().
202      *
203      * @return the next char (without consuming it) or END_OF_STREAM
204      */
205     int lookAhead() throws IOException {
206         if (lookaheadChar == UNDEFINED) {
207             lookaheadChar = super.read();
208         }
209         return lookaheadChar;
210     }
211 
212 
213     /**
214      * Returns the nof line read
215      *
216      * @return the current-line-number (or -1)
217      */
218     int getLineNumber() {
219         return lineCounter > -1 ? lineCounter : -1;
220     }
221 
222     /**
223      * Unsupported.
224      * @throws UnsupportedOperationException if invoked
225      */
226     @Override
227     public boolean markSupported() {
228         throw new UnsupportedOperationException("CSV has no reason to implement this");
229     }
230 
231 }