package org.basex.io.in;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import org.basex.io.IO;
import org.basex.util.Token;
import org.basex.util.TokenBuilder;
/**
* This class provides a convenient access to text input.
* The input encoding will be guessed by analyzing the first bytes.
*
* @author BaseX Team 2005-12, BSD License
* @author Christian Gruen
*/
public class TextInput extends InputStream {
/** Input file. */
private IO input;
/** Input streams. */
private BufferInput[] inputs = new BufferInput[1];
/** Input pointer. */
private int ip;
/** Current line. */
private int line = 1;
/** Buffer with most recent characters. */
private final int[] last = new int[16];
/** Read position. */
private int lp;
/** Backward pointer. */
private int pp;
/**
* Constructor.
* @param is input stream
* @throws IOException I/O exception
*/
TextInput(final InputStream is) throws IOException {
inputs[0] = is instanceof BufferInput ? (BufferInput) is :
new BufferInput(is);
inputs[0].encoding();
}
/**
* Constructor.
* @param in file reference
* @throws IOException I/O exception
*/
public TextInput(final IO in) throws IOException {
this(in.buffer());
input = in;
}
/**
* Sets a new encoding.
* @param e encoding
* @throws IOException I/O exception
*/
public void encoding(final String e) throws IOException {
inputs[0].encoding(e);
}
/**
* Returns the IO reference.
* @return file reference
*/
public IO input() {
return input;
}
/**
* Jumps the specified number of characters back.
* @param p number of characters
*/
public void prev(final int p) {
pp -= p;
pos();
}
@Override
public int read() throws IOException {
if(pp != 0) return last[lp + pp++ & 0x0F];
int ch = inputs[ip].readChar();
while(ch == -1 && ip != 0) ch = inputs[--ip].readChar();
last[lp++] = ch;
lp &= 0x0F;
if(ip == 0 && ch == '\n') ++line;
return ch;
}
/**
* Inserts some bytes in the input stream.
* @param val values to insert
* @param s add spaces
* @return true if everything went alright
* @throws IOException I/O exception
*/
public boolean add(final byte[] val, final boolean s) throws IOException {
if(s) add(new ArrayInput(Token.SPACE));
add(new ArrayInput(val));
if(s) add(new ArrayInput(Token.SPACE));
return ip < 32;
}
/**
* Inserts a cached input buffer.
* @param ci buffer to be added
* @throws IOException I/O exception
*/
private void add(final ArrayInput ci) throws IOException {
if(++ip == inputs.length) inputs = Arrays.copyOf(inputs, ip << 1);
inputs[ip] = ci;
ci.encoding();
}
@Override
public void close() throws IOException {
inputs[0].close();
}
/**
* Returns the current file position.
* @return file position
*/
public int pos() {
return Math.max(0, inputs[0].size() + pp);
}
/**
* Returns the current line.
* @return line
*/
public int line() {
return line;
}
/**
* Returns the file length.
* @return file position
*/
public long length() {
return inputs[0].length();
}
/**
* Retrieves and returns the whole text and closes the stream.
* @return contents
* @throws IOException I/O exception
*/
public byte[] content() throws IOException {
// guess input size
final int l = Math.max(32, input == null ? 0 : (int) input.length());
final TokenBuilder tb = new TokenBuilder(l);
try {
for(int ch; (ch = read()) != -1;) tb.add(ch);
} finally {
close();
}
return tb.finish();
}
}