001: package fri.patterns.interpreter.parsergenerator.lexer;
002:
003: import java.io.*;
004:
005: /**
006: Lexer input wrapper. The input can be InputStream, Reader, File, String or StringBuffer.
007: If Input is InputStream, no Reader will be used, which means that the input reads bytes
008: and not characters.
009: <p>
010: The read() methode returns EOF (-1) when there is no more input.
011:
012: @author (c) 2002, Fritz Ritzberger
013: */
014:
015: class Input {
016: public static final int EOF = -1;
017: private InputStream inputStream;
018: private Reader reader;
019: private int[] buffer;
020: private int readPos, readLen;
021: private int readOffset;
022: private boolean eof = false;
023: private boolean buffering = false;
024:
025: Input(Object input) throws IOException {
026: if (input instanceof InputStream)
027: this .inputStream = input instanceof BufferedInputStream ? (InputStream) input
028: : new BufferedInputStream((InputStream) input);
029: else if (input instanceof Reader)
030: this .reader = input instanceof BufferedReader ? (Reader) input
031: : new BufferedReader((Reader) input);
032: else if (input instanceof File)
033: this .reader = new BufferedReader(new FileReader(
034: (File) input));
035: else if (input instanceof StringBuffer
036: || input instanceof String)
037: this .reader = new StringReader(input.toString());
038: else
039: throw new IllegalArgumentException("Unknown input object: "
040: + (input != null ? input.getClass().getName()
041: : "null"));
042: }
043:
044: /** Returns next character or byte from input. Closes input when EOF is reached. */
045: public int read() throws IOException {
046: int i;
047:
048: if (readLen > readPos) { // use buffer if buffer is not at end
049: i = buffer[readPos];
050: readPos++;
051: return i;
052: }
053:
054: if (eof)
055: return EOF;
056:
057: // read one character from input
058: i = (reader != null) ? reader.read() : inputStream.read();
059:
060: // recognize end of input
061: if (i == -1) {
062: eof = true;
063:
064: try {
065: if (reader != null)
066: reader.close();
067: else
068: inputStream.close();
069: } catch (IOException e) {
070: }
071: } else {
072: readOffset++;
073: convertInput(i); // input hook for subclasses
074:
075: if (buffering) // store character if in buffering state
076: storeRead(i);
077: else
078: readPos = readLen = 0;
079: }
080:
081: return i;
082: }
083:
084: /** Read a lookahead character and reset mark after. */
085: public int peek() throws IOException {
086: int mark = getMark();
087: int c = read();
088: setMark(mark);
089: return c;
090: }
091:
092: /** Override this to buffer lines or convert any read int. */
093: protected int convertInput(int i) {
094: return i;
095: }
096:
097: /** Returns the offset of the last scanned char/byte, less or equal the read offset. */
098: public int getScanOffset() {
099: return getReadOffset() - getUnreadLength();
100: }
101:
102: /** Returns the offset of the last read char/byte (bigger or equal to scan offset). */
103: public int getReadOffset() {
104: return readOffset;
105: }
106:
107: /** Get the current read mark and turn on buffering. The mark is a relative offset, not an absolute read position. */
108: public int getMark() {
109: buffering = true;
110: return readPos;
111: }
112:
113: /** Set read position to passed mark. Needed when trying more scan items. This does not turn on buffering like getMark()! */
114: public void setMark(int mark) {
115: readPos = mark;
116: }
117:
118: /** Resolve buffer, skip rest of unread int's to buffer start. */
119: public void resolveBuffer() {
120: buffering = false;
121:
122: if (readLen > readPos) {
123: if (readPos > 0) { // copy unread buffer to bottom
124: int diff = getUnreadLength();
125: System.arraycopy(buffer, readPos, buffer, 0, diff);
126: readLen = diff;
127: readPos = 0;
128: }
129: } else {
130: readPos = readLen = 0;
131: }
132: }
133:
134: // store the int to buffer
135: private void storeRead(int i) {
136: if (buffer == null) // allocate buffer
137: buffer = new int[128];
138:
139: if (readPos == buffer.length) { // reallocate buffer as it is too small
140: //System.err.println("enlarging lexer buffer from "+buffer.length);
141: int[] old = buffer;
142: // the buffer must not be as long as the input, it just serves as lookahead buffer.
143: buffer = new int[old.length * 2];
144: System.arraycopy(old, 0, buffer, 0, old.length);
145: }
146:
147: if (readPos != readLen)
148: throw new IllegalStateException(
149: "Can not read to buffer when it was not read empty!");
150:
151: buffer[readPos] = i;
152: readPos++;
153: readLen++;
154: }
155:
156: /** Returns the part of the buffer that was not yet consumed. */
157: public int[] getUnreadBuffer() {
158: int diff;
159: if (buffer == null || (diff = getUnreadLength()) <= 0)
160: return new int[0];
161:
162: int[] ret = new int[diff];
163: System.arraycopy(buffer, readPos, ret, 0, diff);
164:
165: return ret;
166: }
167:
168: /** Returns the length that was not yet consumed. */
169: protected int getUnreadLength() {
170: return readLen - readPos;
171: }
172:
173: }
|