package hep.io.mcfio;
import hep.io.xdr.XDRBufferedRandomAccessFile;
import hep.io.xdr.XDRInputStream;
import hep.io.xdr.XDRDataInput;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
/**
* A class for reading MCFIO files
*
* @author Tony Johnson (tonyj@slac.stanford.edu)
* @version $Id: MCFIOReader.java 13660 2009-10-09 23:26:27Z tonyj $
*/
public class MCFIOReader implements MCFIOConstants
{
private EventTable eventTable;
private FileHeader fileHeader;
private XDRDataInput xdr;
private int currentEvent; // index in eventTable of next event
public MCFIOReader(String file) throws IOException
{
init(new XDRBufferedRandomAccessFile(file,true,16192));
}
public MCFIOReader(InputStream input) throws IOException
{
init(new XDRInputStream(input));
}
/**
* Get the comment associated with the file
*/
public String getComment()
{
return fileHeader.getComment();
}
/**
* Get the date when the file was created
*/
public String getDate()
{
return fileHeader.getDate();
}
/**
* The actual number of events on the file
*/
public int getNumberOfEvents()
{
return fileHeader.getNumberOfEvents();
}
/**
* The expected number of events in the file
*/
public int getNumberOfEventsExpected()
{
return fileHeader.getNumberOfEventsExpected();
}
/**
* Get the title of the file
*/
public String getTitle()
{
return fileHeader.getTitle();
}
public void close() throws IOException
{
if (xdr instanceof RandomAccessFile)
{
((RandomAccessFile) xdr).close();
}
else
{
((InputStream) xdr).close();
}
fileHeader = null;
eventTable = null;
}
/**
* Skip events
* @param nEvents The number of events to skip. Must be >= 0.
*/
public void skip(int nEvents) throws IOException
{
if (nEvents < 0) throw new IllegalArgumentException("nEvents must be >= 0");
int eventsLeftToSkip = nEvents;
while (eventsLeftToSkip > 0)
{
if (eventsLeftToSkip < eventTable.numevts()-currentEvent)
{
currentEvent += eventsLeftToSkip;
eventsLeftToSkip = 0;
}
else
{
eventsLeftToSkip -= eventTable.numevts()-currentEvent;
readNextEventTable();
}
}
}
private void readNextEventTable() throws IOException
{
int next = eventTable.nextTable();
if (next <= 0) throw new EOFException();
skipTo(next);
eventTable.read(xdr);
currentEvent = 0;
}
/**
* Get the next event.
* Calling this method does not necessarily mean the full
* event data will be read. Initially only the event number,
* store number, run number, and trigger mask are available.
* Once one of the methods requiring information about the
* blocks inside the event are called the entire event will
* be read. Thus it is possible to loop over events looking
* for events which satisfy some criterion, without actually
* paying the overhead of decoding the whole event.
*/
public MCFIOEvent nextEvent() throws IOException
{
if (currentEvent >= eventTable.numevts())
{
// read the next event table
readNextEventTable();
}
return new EventHeader(currentEvent++);
}
public void rewind() throws IOException
{
if (xdr instanceof RandomAccessFile)
{
((RandomAccessFile) xdr).seek(0);
init(xdr);
}
else
throw new IOException("Rewind not supported");
}
/**
* Override this method to create user defined blocks
*/
protected MCFIOBlock createUserBlock(int id) throws IOException
{
throw new IOException("Unknown user block " + id);
}
private void init(XDRDataInput xdr) throws IOException
{
this.xdr = xdr;
// Read the file header
fileHeader = new FileHeader();
fileHeader.read(xdr);
// Read the first event table
eventTable = new EventTable();
eventTable.read(xdr);
currentEvent = 0;
}
private void skipTo(int target) throws IOException
{
if (xdr instanceof RandomAccessFile)
{
RandomAccessFile random = (RandomAccessFile) xdr;
random.seek(target);
}
else
{
XDRInputStream input = (XDRInputStream) xdr;
int pos = (int) input.getBytesRead();
if (pos < target)
input.skipBytes(target - pos);
else if (pos > target)
throw new MCFIOException("Cannot skip backwards in sequential file: " + pos + " " + target);
}
}
/**
* A private read-only implementation of MCFIOEvent.
* We try to be slightly smart here. Some of the event
* information is available directly in the event table,
* so we only need to actually read the event if the user
* asks for information which is not already available.
* Thus if the user looks at the event/run/store/trigMask
* and decides they are not interested in the rest of the
* event, we never actually need to read the event at all.
*/
private class EventHeader extends MCFIOBlock implements MCFIOEvent
{
private int[] blockIds;
private int[] blockPtrs;
private boolean haveRead;
private int evtnum;
private int nBlocks;
private int pointer;
private int runnum;
private int storenum;
private int trigMask;
EventHeader(int index) throws IOException
{
super(EVENTHEADER);
haveRead = false;
evtnum = eventTable.evtnum(index);
runnum = eventTable.runnum(index);
storenum = eventTable.storenum(index);
trigMask = eventTable.trigMask(index);
pointer = eventTable.ptrEvent(index);
}
public MCFIOBlock getBlock(int index) throws IOException
{
if (!haveRead)
readEvent();
skipTo(blockPtrs[index]);
MCFIOBlock block = createUserBlock(blockIds[index]);
block.read(xdr);
return block;
}
public int getBlockID(int index) throws IOException
{
if (!haveRead)
readEvent();
return blockIds[index];
}
public int getEventNumber()
{
return evtnum;
}
public int getNBlocks() throws IOException
{
if (!haveRead)
readEvent();
return nBlocks;
}
public int getRunNumber()
{
return runnum;
}
public int getStoreNumber()
{
return storenum;
}
public int getTrigMask()
{
return trigMask;
}
public void read(XDRDataInput xdr) throws IOException
{
super.read(xdr);
if (fVersion > 2)
throw new MCFIOException("Unsupported version " + version + " for EventHeader");
evtnum = xdr.readInt();
storenum = xdr.readInt();
runnum = xdr.readInt();
trigMask = xdr.readInt();
nBlocks = xdr.readInt();
int dimBlocks = xdr.readInt();
if (fVersion >= 2)
{
int nNTuples = xdr.readInt();
int dimNTuples = xdr.readInt();
if (nNTuples > 0)
throw new IOException("NTuples not supported");
}
blockIds = xdr.readIntArray(null);
blockPtrs = xdr.readIntArray(null);
// It appears that the blocks are a sort of 0 terminated list
for (int i = 0; i < nBlocks; i++)
if (blockPtrs[i] == 0)
nBlocks = i;
}
public String toString()
{
StringBuffer b = new StringBuffer();
b.append("Run ");
b.append(runnum);
b.append(" store ");
b.append(storenum);
b.append(" event ");
b.append(evtnum);
b.append(" mask ");
b.append(trigMask);
if (haveRead)
{
b.append(" blocks [");
for (int i = 0;;)
{
b.append(blockIds[i]);
if (++i == nBlocks)
break;
b.append(",");
}
b.append("]");
}
return b.toString();
}
private void readEvent() throws IOException
{
skipTo(pointer);
read(xdr);
haveRead = true;
}
}
}