001/*
002 *  gnu/regexp/CharIndexedReader.java
003 *  Copyright (C) 1998-2001 Wes Biggs
004 *
005 *  This library is free software; you can redistribute it and/or modify
006 *  it under the terms of the GNU Lesser General Public License as published
007 *  by the Free Software Foundation; either version 2.1 of the License, or
008 *  (at your option) any later version.
009 *
010 *  This library is distributed in the hope that it will be useful,
011 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
012 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
013 *  GNU Lesser General Public License for more details.
014 *
015 *  You should have received a copy of the GNU Lesser General Public License
016 *  along with this program; if not, write to the Free Software
017 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
018 */
019
020package gnu.regexp;
021import java.io.InputStream;
022import java.io.BufferedInputStream;
023import java.io.IOException;
024
025// TODO: move(x) shouldn't rely on calling next() x times
026
027class CharIndexedInputStream implements CharIndexed {
028    private static final int BUFFER_INCREMENT = 1024;
029    private static final int UNKNOWN = Integer.MAX_VALUE; // value for end
030    
031    private BufferedInputStream br;
032
033    // so that we don't try to reset() right away
034    private int index = -1;
035
036    private int bufsize = BUFFER_INCREMENT;
037
038    private int end = UNKNOWN;
039
040    private char cached = OUT_OF_BOUNDS;
041
042    // Big enough for a \r\n pair
043    // lookBehind[0] = most recent
044    // lookBehind[1] = second most recent
045    private char[] lookBehind = new char[] { OUT_OF_BOUNDS, OUT_OF_BOUNDS }; 
046    
047    CharIndexedInputStream(InputStream str, int index) {
048        if (str instanceof BufferedInputStream) br = (BufferedInputStream) str;
049        else br = new BufferedInputStream(str,BUFFER_INCREMENT);
050        next();
051        if (index > 0) move(index);
052    }
053    
054    private boolean next() {
055        if (end == 1) return false;
056        end--; // closer to end
057
058        try {
059            if (index != -1) {
060                br.reset();
061            }
062            int i = br.read();
063            br.mark(bufsize);
064            if (i == -1) {
065                end = 1;
066                cached = OUT_OF_BOUNDS;
067                return false;
068            }
069            cached = (char) i;
070            index = 1;
071        } catch (IOException e) { 
072            e.printStackTrace();
073            cached = OUT_OF_BOUNDS;
074            return false; 
075        }
076        return true;
077    }
078    
079    public char charAt(int index) {
080        if (index == 0) {
081            return cached;
082        } else if (index >= end) {
083            return OUT_OF_BOUNDS;
084        } else if (index == -1) {
085            return lookBehind[0];
086        } else if (index == -2) {
087            return lookBehind[1];
088        } else if (index < -2) {
089            return OUT_OF_BOUNDS;
090        } else if (index >= bufsize) {
091            // Allocate more space in the buffer.
092            try {
093                while (bufsize <= index) bufsize += BUFFER_INCREMENT;
094                br.reset();
095                br.mark(bufsize);
096                br.skip(index-1);
097            } catch (IOException e) { }
098        } else if (this.index != index) {
099            try {
100                br.reset();
101                br.skip(index-1);
102            } catch (IOException e) { }
103        }
104        char ch = OUT_OF_BOUNDS;
105        
106        try {
107            int i = br.read();
108            this.index = index+1; // this.index is index of next pos relative to charAt(0)
109            if (i == -1) {
110                // set flag that next should fail next time?
111                end = index;
112                return ch;
113            }
114            ch = (char) i;
115        } catch (IOException ie) { }
116        
117        return ch;
118    }
119    
120    public boolean move(int index) {
121        // move read position [index] clicks from 'charAt(0)'
122        boolean retval = true;
123        while (retval && (index-- > 0)) retval = next();
124        return retval;
125    }
126    
127    public boolean isValid() {
128        return (cached != OUT_OF_BOUNDS);
129    }
130}
131