blob: 5296cc50ed0938be112dc1e3d57577023d563e5a [file] [log] [blame]
/**
* Copyright (c) 2008-2011, http://www.snakeyaml.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.yaml.snakeyaml.reader;
import java.io.IOException;
import java.io.Reader;
import java.nio.charset.Charset;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.yaml.snakeyaml.error.Mark;
import org.yaml.snakeyaml.error.YAMLException;
import org.yaml.snakeyaml.scanner.Constant;
/**
* Reader: checks if characters are in allowed range, adds '\0' to the end.
*/
public class StreamReader {
// NON_PRINTABLE changed from PyYAML: \uFFFD excluded because Java returns
// it in case of data corruption
final static Pattern NON_PRINTABLE = Pattern
.compile("[^\t\n\r\u0020-\u007E\u0085\u00A0-\uD7FF\uE000-\uFFFC]");
private String name;
private final Reader stream;
private int pointer = 0;
private boolean eof = true;
private String buffer;
private int index = 0;
private int line = 0;
private int column = 0;
private char[] data;
public StreamReader(String stream) {
this.name = "<string>";
this.buffer = "";
checkPrintable(stream);
this.buffer = stream + "\0";
this.stream = null;
this.eof = true;
this.data = null;
}
public StreamReader(Reader reader) {
this.name = "<reader>";
this.buffer = "";
this.stream = reader;
this.eof = false;
this.data = new char[1024];
this.update(1);
}
void checkPrintable(CharSequence data) {
Matcher em = NON_PRINTABLE.matcher(data);
if (em.find()) {
int position = this.index + this.buffer.length() - this.pointer + em.start();
throw new ReaderException(name, position, em.group().charAt(0),
"special characters are not allowed");
}
}
public Mark getMark() {
return new Mark(name, this.index, this.line, this.column, this.buffer, this.pointer);
}
public void forward() {
forward(1);
}
/**
* read the next length characters and move the pointer.
*
* @param length
*/
public void forward(int length) {
if (this.pointer + length + 1 >= this.buffer.length()) {
update(length + 1);
}
char ch = 0;
for (int i = 0; i < length; i++) {
ch = this.buffer.charAt(this.pointer);
this.pointer++;
this.index++;
if (Constant.LINEBR.has(ch) || (ch == '\r' && buffer.charAt(pointer) != '\n')) {
this.line++;
this.column = 0;
} else if (ch != '\uFEFF') {
this.column++;
}
}
}
public char peek() {
return this.buffer.charAt(this.pointer);
}
/**
* Peek the next index-th character
*
* @param index
* @return
*/
public char peek(int index) {
if (this.pointer + index + 1 > this.buffer.length()) {
update(index + 1);
}
return this.buffer.charAt(this.pointer + index);
}
/**
* peek the next length characters
*
* @param length
* @return
*/
public String prefix(int length) {
if (this.pointer + length >= this.buffer.length()) {
update(length);
}
if (this.pointer + length > this.buffer.length()) {
return this.buffer.substring(this.pointer);
}
return this.buffer.substring(this.pointer, this.pointer + length);
}
/**
* prefix(length) immediately followed by forward(length)
*/
public String prefixForward(int length) {
final String prefix = prefix(length);
this.pointer += length;
this.index += length;
// prefix never contains new line characters
this.column += length;
return prefix;
}
private void update(int length) {
if (!this.eof) {
this.buffer = buffer.substring(this.pointer);
this.pointer = 0;
do {
String rawData = null;
int converted = -2;
try {
converted = this.stream.read(data);
} catch (IOException ioe) {
throw new YAMLException(ioe);
}
if (converted > 0) {
rawData = new String(data, 0, converted);
checkPrintable(rawData);
this.buffer += rawData;
} else {
this.eof = true;
this.buffer += "\0";
break;
}
} while (this.buffer.length() < length);
}
}
public int getColumn() {
return column;
}
public Charset getEncoding() {
return Charset.forName(((UnicodeReader) this.stream).getEncoding());
}
public int getIndex() {
return index;
}
public int getLine() {
return line;
}
}