/*$Id$*/
package at.jku.sii.sqlitereader.io;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import at.jku.sii.sqlitereader.annotator.AnnotatedAdvancedDataInputBase;
import at.jku.sii.sqlitereader.annotator.Annotator;
import at.jku.sii.sqlitereader.annotator.OffsetAnnotator;
public class ChainedArrayDataInput extends AnnotatedAdvancedDataInputBase implements Sizeable {
private final ArrayDataInput[] ins;
private final int size;
private final long[] offsets;
private int current;
private int currentIn;
private final Annotator annotator;
private final DataInputStream din;
public ChainedArrayDataInput(Collection<ArrayDataInput> inputs) {
this(inputs.toArray(new ArrayDataInput[0]));
}
public ChainedArrayDataInput(ArrayDataInput... inputs) {
this.ins = inputs;
int sum = 0;
this.offsets = new long[this.ins.length];
for (int i = 0; i < this.ins.length; ++i) {
this.offsets[i] = sum;
sum += this.ins[i].size();
}
this.size = sum;
this.annotator = new Annotator() {
@Override
public Annotator createSubAnnotator(long offset) {
return new OffsetAnnotator(offset, this);
}
@Override
public void annotate(long pos, int length, String text, Object... value) {
ChainedArrayDataInput.this.annotateChained(pos, length, text, value);
}
};
this.din = new DataInputStream(new InputStream() {
@Override
public int read() throws IOException {
return ChainedArrayDataInput.this.readChained();
}
});
}
protected int readChained() {
ArrayDataInput in = this.ins[this.currentIn];
this.current++;
long relative = this.current - this.offsets[this.currentIn];
while (relative > in.size() && this.currentIn < this.ins.length) { // switch
this.currentIn++;
in = this.ins[this.currentIn];
in.seek(0);
relative = this.current - this.offsets[this.currentIn];
}
if (relative > in.size())
return -1; // EOF
return in.read();
}
protected void annotateChained(long pos, int length, String annotation, Object... value) {
int input = this.findStart(pos);
pos -= this.offsets[input]; // to relative
while (length > 0) {
ArrayDataInput block = this.ins[input];
final int blockSize = block.size();
block.annotate(pos, Math.min(length, blockSize), annotation, value);
pos -= blockSize;
length -= blockSize;
}
}
private int findStart(long pos) {
// fast version, its the current one
if (pos >= this.offsets[this.currentIn] && (this.currentIn >= this.offsets.length - 1 || pos < this.offsets[this.currentIn + 1]))
return this.currentIn;
for (int i = 1; i < this.offsets.length; ++i)
if (pos < this.offsets[i]) // too big -> within the last one
return i - 1;
return this.offsets.length - 1;
}
@Override
public final int size() {
return this.size;
}
@Override
public long currentPosition() {
return this.current;
}
@Override
protected final DataInput get() {
return this.din;
}
public final void seek(long pos) {
this.current += pos;
this.currentIn = this.findStart(pos);
this.ins[this.currentIn].seek(this.currentRelative());
}
private long currentRelative() {
return this.current - this.offsets[this.currentIn];
}
@Override
protected Annotator getAnnotator() {
return this.annotator;
}
}