/*
* This file is a part of Alchemy OS project.
* Copyright (C) 2011-2013, Sergey Basalaev <sbasalaev@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package alchemy.io;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.UTFDataFormatException;
/**
* Reader of UTF-encoded data.
* Also this reader allows one to read a single line of text
* and to get a number of the current line.
*
* @author Sergey Basalaev
*/
public final class UTFReader extends Reader {
private InputStream input;
private int linenum;
public UTFReader(InputStream s) {
input = s;
}
public int read() throws IOException {
if (input == null) throw new IOException("Stream is closed");
int b1 = input.read();
if (b1 < 0) return -1;
if (b1 <= 0x7f) {
if (b1 == '\n') linenum++;
return b1;
}
if (b1 < 0xC0) throw new UTFDataFormatException("Malformed UTF");
int b2 = input.read();
if (b2 < 0) throw new UTFDataFormatException("Unfinished UTF char at EOF");
if ((b2 & 0xC0) != 0x80) throw new UTFDataFormatException("Malformed UTF");
if (b1 < 0xE0) {
return ((b1 & 0x1f) << 6) | (b2 & 0x3f);
}
int b3 = input.read();
if (b3 < 0) throw new UTFDataFormatException("Unfinished UTF char at EOF");
if ((b3 & 0xC0) != 0x80) throw new UTFDataFormatException("Malformed UTF");
return ((b1 & 0x0f) << 12) | ((b2 & 0x3f) << 6) | (b3 & 0x3f);
}
public int read(char[] cbuf, int off, int len) throws IOException {
if (off+len > cbuf.length || off < 0)
throw new IndexOutOfBoundsException();
if (len == 0) return 0;
int reallen = 0;
while (reallen < len) {
int ch = read();
if (ch < 0) break;
cbuf[off+reallen] = (char)ch;
reallen++;
}
if (reallen == 0) return -1;
return reallen;
}
public void close() throws IOException {
if (input == null) return;
input.close();
input = null;
}
/**
* Reads a single line of text.
* @return
* characters until first <code>'\n'</code> or EOF
* as a string; <code>null</code> if stream is at end
* @throws IOException if an I/O error occurs
*/
public String readLine() throws IOException {
int ch = read();
if (ch < 0) return null;
StringBuffer sb = new StringBuffer();
while (ch > 0 && ch != '\n') {
sb.append((char)ch);
ch = read();
}
return sb.toString();
}
/**
* Returns number of the current line.
* @return current line number
*/
public int lineNumber() {
return linenum;
}
}