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