001/*
002 *                    BioJava development code
003 *
004 * This code may be freely distributed and modified under the
005 * terms of the GNU Lesser General Public Licence.  This should
006 * be distributed with the code.  If you do not have a copy,
007 * see:
008 *
009 *      http://www.gnu.org/copyleft/lesser.html
010 *
011 * Copyright for this code is held jointly by the individual
012 * authors.  These should be listed in @author doc comments.
013 *
014 * For more information on the BioJava project and its aims,
015 * or to join the biojava-l mailing list, visit the home page
016 * at:
017 *
018 *      http://www.biojava.org/
019 *
020 */
021package org.biojava.utils.io;
022
023import java.io.BufferedReader;
024import java.io.IOException;
025import java.io.Reader;
026
027/**
028 * @author Thomas Down
029 */
030    public class CountedBufferedReader extends BufferedReader {
031        private final static int DEFAULT_BUFFER_SIZE = 1 << 14;
032
033        private long position;
034
035        private Reader stream;
036        private char[] buffer;
037        private int buffPos;
038        private int buffFill;
039
040        private boolean reachedEOF = false;
041
042        private int mark = -1;
043        private int markLimit = -1;
044
045        public long getFilePointer() {
046            return position;
047        }
048
049        public CountedBufferedReader(Reader stream) {
050            super(new Reader() {
051                public void close() {}
052                public int read(char[] cbuf, int off, int len) { return 0; }
053            });
054
055            this.stream = stream;
056            this.buffer = new char[DEFAULT_BUFFER_SIZE];
057            position = 0;
058            buffPos = 0;
059            buffFill = 0;
060        }
061
062        public void close()
063            throws IOException
064        {
065            stream.close();
066            stream = null;
067        }
068
069        public int read() 
070            throws IOException
071        {
072            if (buffPos >= buffFill)
073                fillBuffer();
074
075            if (reachedEOF) {
076                return -1;
077            } else {
078                position++;
079                return buffer[buffPos++];
080            }
081        }
082
083        private int peek()
084            throws IOException
085        {
086            if (buffPos >= buffFill)
087                fillBuffer();
088
089            if (reachedEOF) {
090                return -1;
091            } else {
092                return buffer[buffPos];
093            }
094        }
095
096        public int read(char[] cbuf)
097            throws IOException
098        {
099            return read(cbuf, 0, cbuf.length);
100        }
101
102        public int read(char[] cbuf, int off, int len) 
103            throws IOException
104        {
105            if (buffPos >= buffFill)
106                fillBuffer();
107
108            if (reachedEOF) {
109                return -1;
110            } else {
111                int toReturn = Math.min(len, buffFill - buffPos);
112                System.arraycopy(buffer, buffPos, cbuf, off, toReturn);
113                buffPos += toReturn;
114                position += toReturn;
115
116                return toReturn;
117            }
118        }
119
120        public boolean ready() 
121            throws IOException
122        {
123            if (buffPos < buffFill)
124                return true;
125            return stream.ready();
126        }
127
128        public long skip(long n)
129            throws IOException
130        {
131            int skipInBuffer;
132            if (n < buffer.length) {
133                skipInBuffer = Math.min((int) n, buffFill - buffPos);
134            } else {
135                skipInBuffer = buffFill - buffPos;
136            }
137            position += skipInBuffer;
138            buffPos += skipInBuffer;
139
140            if (n > skipInBuffer) {
141                long skippedInStream;
142
143                if (mark >= 0) {
144                    // Yuck, fix this...
145                    char[] dummy = new char[(int) (n - skipInBuffer)];
146                    skippedInStream = read(dummy); 
147                } else {
148                    skippedInStream = stream.skip(n - skipInBuffer);
149                }
150
151                position += skippedInStream;
152                return skippedInStream + skipInBuffer;
153            } else {
154                return skipInBuffer;
155            }
156        }
157
158        public boolean markSupported() {
159            return true;
160        }
161
162        public void mark(int limit)
163            throws IOException
164        {
165            //      System.err.println("*** Mark");
166
167            if (limit + 1> buffer.length) {
168                char[] newBuffer = new char[limit + 1];
169                System.arraycopy(buffer, buffPos, newBuffer, 0, buffFill - buffPos);
170                buffer = newBuffer;
171                buffFill = buffFill - buffPos;
172                buffPos = 0;
173            } else if (buffPos + limit > buffer.length) {
174                System.arraycopy(buffer, buffPos, buffer, 0, buffFill - buffPos);
175                buffFill = buffFill - buffPos;
176                buffPos = 0;
177            }
178
179            mark = buffPos;
180            markLimit = limit;
181        }
182
183        public void reset()
184            throws IOException
185        {
186            //      System.err.println("*** Reset");
187
188            if (mark < 0)
189                throw new IOException("The mark is not currently in scope");
190
191            position = position - buffPos + mark;
192            buffPos = mark;
193        }
194
195        public String readLine()
196            throws IOException 
197        {
198            String line = null;
199            
200      while(!reachedEOF) {
201        for(int i = buffPos; i < buffFill; i++) {
202          char c = buffer[i];
203          if(c == '\n' || c == '\r') {
204            int len = i - buffPos;
205            String bit = new String(buffer, buffPos, len);
206            position += len;
207            if(line == null) {
208              line = bit;
209            } else {
210              line += bit;
211            }
212            buffPos = i;
213            read();
214            char d = (char) peek();
215            if(c == '\r' && d == '\n') {
216              read();
217            }
218            
219            return line;
220          }
221        }
222        int len = buffFill - buffPos;
223        String bit = new String(buffer, buffPos, len);
224        position += len;
225        buffPos = buffFill;
226        if(line == null) {
227          line = bit;
228        } else {
229          line += bit;
230        }
231        fillBuffer();
232      }
233      
234      return line;
235        }
236
237        private void fillBuffer()
238            throws IOException
239        {
240            // System.err.println("*** Fill buffer");
241
242            if (mark < 0) {
243                buffFill = stream.read(buffer);
244                if (buffFill == -1) {
245                    buffFill = 0;
246                    reachedEOF = true;
247                } 
248                // System.out.println("Filled buffer: " + buffFill);
249                
250                buffPos = 0;
251            } else {
252                if (buffPos >= (markLimit + mark)) {
253                    // Mark's gone out of scope -- wheee!
254                    mark = -1;
255                    markLimit = -1;
256                    fillBuffer();
257                    return;
258                }
259
260                System.arraycopy(buffer, mark, buffer, 0, buffFill - mark);
261                buffFill = buffFill - mark;
262                mark = 0;
263                buffPos = buffFill;
264                int newChars = stream.read(buffer, buffFill, buffer.length - buffFill);
265                if (newChars == -1) {
266                    reachedEOF = true;
267                } else {
268                    buffFill = buffFill + newChars;
269                }
270            }
271        }
272    }
273