package org.openntf.domino.nsfdata.structs.cd;
import java.io.ByteArrayOutputStream;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.AbstractSequentialList;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;
import org.openntf.domino.exceptions.UnimplementedException;
import org.openntf.domino.nsfdata.structs.SIG;
public class CData extends AbstractSequentialList<CDRecord> implements Externalizable {
private static final long serialVersionUID = 1L;
private ByteBuffer data_;
@SuppressWarnings("unused")
private int startingPosition_;
private transient List<CDRecord> fetched_;
public CData(final ByteBuffer data) {
data_ = data.duplicate();
data_.order(ByteOrder.LITTLE_ENDIAN);
startingPosition_ = data_.position();
}
public CData(final byte[] data) {
this(ByteBuffer.wrap(data));
}
@Override
public ListIterator<CDRecord> listIterator(final int index) {
return new CDataIterator(index);
}
@Override
public int size() {
// We'll have to process the whole thing to get this value
int breaker = 0;
while (data_.hasRemaining()) {
fetchNextRecord();
if (breaker > 100000) {
throw new RuntimeException("Too many records!");
}
}
return fetched_.size();
}
private CDRecord fetchNextRecord() {
if (!data_.hasRemaining()) {
throw new NoSuchElementException();
}
// System.out.println("opening pos: " + data_.position());
// System.out.println("opening capacity: " + data_.capacity());
// Peek at the next couple bytes to get SIG and find the appropriate record type
SIG sig = CDSignature.sigForData(data_.duplicate().order(ByteOrder.LITTLE_ENDIAN));
// System.out.println("making " + sig);
// Now the ByteBuffer is positioned at the start of the data
// Create a view starting at the start of the data and going the length of the data
ByteBuffer recordData = data_.duplicate().order(ByteOrder.LITTLE_ENDIAN);
long recordSize = sig.getRecordLength() + (sig.getRecordLength() % 2);
recordData.limit((int) (recordData.position() + recordSize));
//CDRecord record = CDRecord.create(sig, recordData);
CDRecord record = null;
try {
record = CDSignature.instanceClassForSig(sig).newInstance();
} catch (Exception e) {
throw e instanceof RuntimeException ? (RuntimeException) e : new RuntimeException(e);
}
record.init(recordData);
// Skip past the data for the next record
try {
// System.out.println("moving forward " + recordSize);
data_.position((int) (data_.position() + recordSize));
} catch (IllegalArgumentException e) {
throw e;
}
if (fetched_ == null) {
fetched_ = new LinkedList<CDRecord>();
}
fetched_.add(record);
return fetched_.get(fetched_.size() - 1);
}
public byte[] getBytes() {
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
for (CDRecord rec : this) {
bos.write(rec.getBytes());
}
return bos.toByteArray();
} catch (IOException ioe) {
throw new RuntimeException(ioe);
}
}
@Override
public void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException {
int capacity = in.readInt();
data_ = ByteBuffer.allocate(capacity);
for (int i = 0; i < capacity; i++) {
data_.put((byte) in.read());
}
}
@Override
public void writeExternal(final ObjectOutput out) throws IOException {
ByteBuffer data = data_.duplicate();
data.position(0);
out.writeInt(data.capacity());
while (data.hasRemaining()) {
out.write(data.get());
}
}
private class CDataIterator implements ListIterator<CDRecord> {
private final int start_;
private int index_;
public CDataIterator(final int start) {
start_ = start;
index_ = start - 1;
}
@Override
public void add(final CDRecord record) {
throw new UnimplementedException();
}
@Override
public boolean hasNext() {
return data_.hasRemaining();
}
@Override
public boolean hasPrevious() {
return index_ > start_;
}
@Override
public CDRecord next() {
if (!hasNext()) {
throw new IndexOutOfBoundsException("No records remaining");
}
index_++;
if (fetched_ == null || index_ == fetched_.size()) {
return fetchNextRecord();
} else {
return fetched_.get(index_);
}
}
@Override
public int nextIndex() {
return index_ + 1;
}
@Override
public CDRecord previous() {
if (index_ <= start_) {
throw new IndexOutOfBoundsException("No previous record");
}
index_--;
return fetched_.get(index_);
}
@Override
public int previousIndex() {
return index_ - 1;
}
@Override
public void remove() {
throw new UnimplementedException();
}
@Override
public void set(final CDRecord record) {
throw new UnimplementedException();
}
}
}