28 modified files
jevio-base/src/main/java/org/jlab/coda/jevio
diff -u -r1.7 -r1.8
--- CompositeData.java 21 Mar 2012 21:24:19 -0000 1.7
+++ CompositeData.java 3 Apr 2013 20:07:46 -0000 1.8
@@ -44,7 +44,6 @@
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.List;
-import java.util.ListIterator;
/**
* This is the class defining the composite data type.
@@ -82,7 +81,7 @@
/** Buffer containing only the data of the composite item (no headers). */
private ByteBuffer dataBuffer;
- /** Length of only data in bytes. */
+ /** Length of only data in bytes (not including padding). */
private int dataBytes;
/** Offset (in 32bit words) in rawBytes to place of data. */
@@ -273,6 +272,177 @@
/**
+ * This method parses an array of raw bytes into an array of CompositeData objects.
+ *
+ * @param rawBytes array of raw bytes to parse
+ * @param byteOrder byte order of raw bytes
+ * @return array of CompositeData objects obtained from parsing rawBytes. If none, return null.
+ * @throws EvioException if null args or bad format of raw data
+ */
+ static public CompositeData[] parse(byte[] rawBytes, ByteOrder byteOrder) throws EvioException {
+
+ boolean debug = false;
+
+ if (rawBytes == null) {
+ throw new EvioException("null argument(s)");
+ }
+
+ if (debug) System.out.println("CompositeData parse: incoming byte order = " + byteOrder);
+
+ if (byteOrder == null) {
+ byteOrder = ByteOrder.BIG_ENDIAN;
+ }
+
+ if (debug) System.out.println("Analyzing composite data:");
+
+ // List containing all parsed CompositeData objects
+ ArrayList<CompositeData> list = new ArrayList<CompositeData>(100);
+ // How many bytes have we read for the current CompositeData object?
+ int byteCount;
+ // Offset into the given rawBytes array for the current CompositeData object
+ int rawBytesOffset = 0;
+ // How many unused bytes are left in the given rawBytes array?
+ int rawBytesLeft = rawBytes.length;
+
+ if (debug) System.out.println(" raw byte count = " + rawBytesLeft);
+
+ // Parse while we still have bytes to read ...
+ while (rawBytesLeft > 0) {
+
+ byteCount = 0;
+
+ // Create and fill new CompositeData object
+ CompositeData cd = new CompositeData();
+ cd.byteOrder = byteOrder;
+
+ // First read the tag segment header
+ cd.tsHeader = EventParser.createTagSegmentHeader(rawBytes, rawBytesOffset, byteOrder);
+ byteCount += 4*(cd.tsHeader.getLength() + 1);
+
+ if (debug) {
+ System.out.println(" tagseg: type = " + cd.tsHeader.getDataType() +
+ ", tag = " + cd.tsHeader.getTag() + ", len = " + cd.tsHeader.getLength());
+ }
+
+ // Hop over tagseg header
+ cd.dataOffset = cd.tsHeader.getHeaderLength();
+
+ // Read the format string it contains
+ String[] strs = BaseStructure.unpackRawBytesToStrings(rawBytes, rawBytesOffset + 4*cd.dataOffset);
+
+ if (strs.length < 1) {
+ throw new EvioException("bad format string data");
+ }
+ cd.format = strs[0];
+
+ if (debug) {
+ System.out.println(" format: " + cd.format);
+ }
+
+ // Chew on format string & spit out array of ints
+ cd.formatInts = compositeFormatToInt(cd.format);
+ if (cd.formatInts.size() < 1) {
+ throw new EvioException("bad format string data");
+ }
+
+ // Hop over tagseg (format string) data
+ cd.dataOffset = cd.tsHeader.getLength() + 1;
+
+ // Read the data bank header
+ cd.bHeader = EventParser.createBankHeader(rawBytes, rawBytesOffset + 4*cd.dataOffset, byteOrder);
+ byteCount += 4*(cd.bHeader.getLength() + 1);
+
+ // Hop over bank header
+ cd.dataOffset += cd.bHeader.getHeaderLength();
+
+ // How many bytes do we skip over at the end?
+ int padding = cd.bHeader.getPadding();
+
+ // How much real data do we have (without padding)?
+ cd.dataBytes = 4*(cd.bHeader.getLength() - (cd.bHeader.getHeaderLength() - 1)) - padding;
+ if (cd.dataBytes < 2) {
+ throw new EvioException("no composite data");
+ }
+
+ if (debug) {
+ System.out.println(" bank: type = " + cd.bHeader.getDataType() +
+ ", tag = " + cd.bHeader.getTag() + ", num = " + cd.bHeader.getNumber());
+ System.out.println(" bank: len (words) = " + cd.bHeader.getLength() +
+ ", data len - padding (bytes) = " + cd.dataBytes);
+ }
+
+ // Make copy of only the rawbytes for this CompositeData object (including padding)
+ cd.rawBytes = new byte[byteCount];
+ System.arraycopy(rawBytes, rawBytesOffset, cd.rawBytes, 0, byteCount);
+
+ // Put only actual data into ByteBuffer object
+ cd.dataBuffer = ByteBuffer.wrap(cd.rawBytes, 4*cd.dataOffset, cd.dataBytes).slice();
+
+ // Turn dataBuffer into list of items and their types
+ cd.process();
+
+ // Add to this CompositeData object to list
+ list.add(cd);
+
+ // Track how many raw bytes we have left to parse
+ rawBytesLeft -= byteCount;
+
+ // Offset into rawBytes of next CompositeData object
+ rawBytesOffset += byteCount;
+ if (debug) System.out.println(" raw byte count = " + rawBytesLeft + ", raw byte offset = " +
+ rawBytesOffset);
+ }
+
+ int size = list.size();
+ if (size > 0) {
+ // Turn list into array
+ CompositeData[] cdArray = new CompositeData[size];
+ return list.toArray(cdArray);
+ }
+
+ return null;
+ }
+
+ /**
+ * This method generates raw bytes of evio format from an array of CompositeData objects.
+ * The returned array consists of gluing together all the individual objects' rawByte arrays.
+ *
+ * @param data array of CompositeData object to turn into bytes
+ * @return array of raw, evio format bytes; null if arg is null or empty
+ * @throws EvioException if data takes up too much memory to store in raw byte array (JVM limit)
+ */
+ static public byte[] generateRawBytes(CompositeData data[]) throws EvioException {
+
+ if (data == null || data.length < 1) {
+ return null;
+ }
+
+ // Get a total length (# bytes)
+ int totalLen = 0, len;
+ for (CompositeData cd : data) {
+ len = cd.getRawBytes().length;
+ if (Integer.MAX_VALUE - totalLen < len) {
+ throw new EvioException("added data overflowed containing structure");
+ }
+ totalLen += len;
+ }
+
+ // Allocate an array
+ byte[] rawBytes = new byte[totalLen];
+
+ // Copy everything in
+ int offset = 0;
+ for (CompositeData cd : data) {
+ len = cd.getRawBytes().length;
+ System.arraycopy(cd.getRawBytes(), 0, rawBytes, offset, len);
+ offset += len;
+ }
+
+ return rawBytes;
+ }
+
+
+ /**
* Method to clone a CompositeData object.
* Deep cloned so no connection exists between
* clone and object cloned.
@@ -363,19 +533,7 @@
* of being part of the format string. */
private ArrayList<Integer> nList = new ArrayList<Integer>(100);
-// private String format;
-// private List<Integer> formatInts;
-// private int formatIndex;
-// private int parenLevel;
-
-// /** The number of times items of a particular type get added. */
-// private int repeat = 1;
-
-// public Data(String format) throws EvioException {
-// this.format = format;
-// this.formatInts = compositeFormatToInt(format);
-// if (formatInts.size() <= 0) throw new EvioException("empty format list");
-// }
+
/** Constructor. */
public Data() {}
@@ -409,30 +567,18 @@
}
/**
- * This method add an "N" or multiplier value to the data
- * in the sequence determined by the format string.
- * Does not needed to be added in sequence with other data.
+ * This method adds an "N" or multiplier value to the data.
+ * It needs to be added in sequence with other data.
* @param N N or multiplier value
*/
synchronized public void addN(int N) {
nList.add(N);
+ dataItems.add(N);
+ dataTypes.add(DataType.INT32);
addBytesToData(4);
}
- /**
- * This method add an array of "N" or multiplier values to the data
- * in the sequence determined by the format string. Does not needed
- * to be added in sequence with other data.
- * @param N array of N or multiplier values
- */
- synchronized public void addN(int[] N) {
- for (int n : N) {
- nList.add(n);
- addBytesToData(4);
- }
- }
-
/**
* Add a signed 32 bit integer to the data.
@@ -1336,7 +1482,8 @@
* This method converts (swaps) a buffer of EVIO composite type between big &
* little endian. It swaps the entire type including the beginning tagsegment
* header, the following format string it contains, the data's bank header,
- * and finally the data itself.
+ * and finally the data itself. The src array may contain an array of
+ * composite type items and all will be swapped.
*
* @param src source data array (of 32 bit words)
* @param srcOff # of bytes offset into source data array
@@ -1346,7 +1493,8 @@
* @param srcOrder the byte order of data in src
*
* @throws EvioException if offsets or length < 0; if src = null;
- * if src = dest and offsets are not the same
+ * if src = dest and offsets are not the same;
+ * if src or dest is too small
*/
public static void swapAll (byte[] src, int srcOff, byte[] dest, int destOff,
int length, ByteOrder srcOrder) throws EvioException {
@@ -1373,93 +1521,117 @@
ByteOrder destOrder = (srcOrder == ByteOrder.BIG_ENDIAN) ?
ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN;
- // Move to beginning of tagseg header
- int srcDataOffset = srcOff;
- int destDataOffset = destOff;
- //System.out.println("srcDataOffset = " + srcDataOffset + ", len (Bytes) = " + (4*length) + ", src array len = " + src.length);
- ByteBuffer srcBuffer = ByteBuffer.wrap( src, srcDataOffset, 4*length);
- ByteBuffer destBuffer = ByteBuffer.wrap(dest, destDataOffset, 4*length);
+
+ // How many unused bytes are left in the src array?
+ int srcBytesLeft = 4*length;
+
+ // How many bytes taken for this CompositeData object?
+ int dataOffset;
+
+ // Wrap input & output arrays in ByteBuffers for convenience
+ ByteBuffer srcBuffer = ByteBuffer.wrap( src, srcOff, 4*length);
+ ByteBuffer destBuffer = ByteBuffer.wrap(dest, destOff, 4*length);
// Here is where we do some of the swapping
srcBuffer.order(srcOrder);
destBuffer.order(destOrder);
- // First read the tag segment header
- TagSegmentHeader tsHeader = EventParser.createTagSegmentHeader(src, srcDataOffset, srcOrder);
- int headerLen = tsHeader.getHeaderLength();
- int dataLength = tsHeader.getLength() - (headerLen - 1);
+ while (srcBytesLeft > 0) {
-//System.out.println("theaderLen = "+headerLen+ ", theader.getLn() = " +
-// tsHeader.getLength()+ ", tdataLen = " + dataLength);
+ // Start here
+ dataOffset = 0;
+//System.out.println("start src offset = " + (srcOff + dataOffset));
- // Oops, no format data
- if (dataLength < 1) {
- throw new EvioException("no format data");
- }
+ // First read the tag segment header
+ TagSegmentHeader tsHeader = EventParser.createTagSegmentHeader(src, srcOff + dataOffset, srcOrder);
+ int headerLen = tsHeader.getHeaderLength();
+ int dataLength = tsHeader.getLength() - (headerLen - 1);
- // Got all we needed from the tagseg header, now swap as it's written out.
- tsHeader.write(destBuffer);
+//System.out.println("tag len = " + tsHeader.getLength() + ", dataLen = " + dataLength);
- // Move to beginning of string data
- srcDataOffset += 4*headerLen;
- destDataOffset += 4*headerLen;
+ // Oops, no format data
+ if (dataLength < 1) {
+ throw new EvioException("no format data");
+ }
- // Read the format string it contains
- String[] strs = BaseStructure.unpackRawBytesToStrings(src, srcDataOffset);
+ // Got all we needed from the tagseg header, now swap as it's written out.
+ tsHeader.write(destBuffer);
- if (strs.length < 1) {
- throw new EvioException("bad format string data");
- }
- String format = strs[0];
+ // Move to beginning of string data
+ dataOffset += 4*headerLen;
- // Transform string format into int array format
- List<Integer> formatInts = compositeFormatToInt(format);
- if (formatInts.size() < 1) {
- throw new EvioException("bad format string data");
- }
+ // Read the format string it contains
+ String[] strs = BaseStructure.unpackRawBytesToStrings(src, srcOff + dataOffset);
- // Char data does not get swapped but needs
- // to be copied if not swapping in place.
- if (!inPlace) {
- System.arraycopy(src, srcDataOffset, dest, destDataOffset, 4*dataLength);
- }
+ if (strs.length < 1) {
+ throw new EvioException("bad format string data");
+ }
+ String format = strs[0];
- // Move to beginning of bank header
- srcDataOffset += 4*dataLength;
- destDataOffset += 4*dataLength;
+ // Transform string format into int array format
+ List<Integer> formatInts = compositeFormatToInt(format);
+ if (formatInts.size() < 1) {
+ throw new EvioException("bad format string data");
+ }
- // Read the data bank header
- BankHeader bHeader = EventParser.createBankHeader(src, srcDataOffset, srcOrder);
- headerLen = bHeader.getHeaderLength();
- dataLength = bHeader.getLength() - (headerLen - 1);
-
-//System.out.println("bheaderLen = "+headerLen+ ", bheader.getLn() = " +
-// bHeader.getLength()+ ", bdataLen = " + dataLength);
-
- // Oops, no data
- if (dataLength < 1) {
- throw new EvioException("no data");
- }
-
- // Adjust data length by switching units from
- // ints to bytes and accounting for padding.
- dataLength = 4*dataLength - bHeader.getPadding();
-
- // Got all we needed from the bank header, now swap as it's written out.
- destBuffer.position(destDataOffset);
- bHeader.write(destBuffer);
-
- // Move to beginning of data
- srcDataOffset += 4*headerLen;
- destDataOffset += 4*headerLen;
- srcBuffer.position(srcDataOffset);
- destBuffer.position(destDataOffset);
+ // Char data does not get swapped but needs
+ // to be copied if not swapping in place.
+ if (!inPlace) {
+ System.arraycopy(src, srcOff + dataOffset,
+ dest, destOff + dataOffset, 4*dataLength);
+ }
+
+ // Move to beginning of bank header
+ dataOffset += 4*dataLength;
+
+ // Read the data bank header
+ BankHeader bHeader = EventParser.createBankHeader(src, srcOff + dataOffset, srcOrder);
+ headerLen = bHeader.getHeaderLength();
+ dataLength = bHeader.getLength() - (headerLen - 1);
+
+//System.out.println("bank len = " + bHeader.getLength() + ", dataLen = " + dataLength);
+
+ // Oops, no data
+ if (dataLength < 1) {
+ throw new EvioException("no data");
+ }
+
+ // Adjust data length by switching units from
+ // ints to bytes and accounting for padding.
+ dataLength = 4*dataLength - bHeader.getPadding();
+
+ // Got all we needed from the bank header, now swap as it's written out.
+ destBuffer.position(destOff + dataOffset);
+ bHeader.write(destBuffer);
+
+ // Move to beginning of data
+ dataOffset += 4*headerLen;
+ srcBuffer.position( srcOff + dataOffset);
+ destBuffer.position(destOff + dataOffset);
+
+ // Swap data
+ swapData(srcBuffer, destBuffer, dataLength, formatInts);
+
+ // Set buffer positions and offset
+ dataOffset += dataLength;
+ srcBuffer.position( srcOff + dataOffset);
+ destBuffer.position(srcOff + dataOffset);
+
+ srcOff += dataOffset;
+ destOff += dataOffset;
+ srcBytesLeft -= dataOffset;
+//System.out.println("bytes left = " + srcBytesLeft);
+//System.out.println("src pos = " + srcBuffer.position() + ", dest pos = " + destBuffer.position());
- // Swap data
- swapData(srcBuffer, destBuffer, dataLength, formatInts);
+ // Oops, things aren't coming out evenly
+ if (srcBytesLeft < 0) {
+ throw new EvioException("bad format");
+ }
+ }
}
+
/**
* This method converts (swaps) an array of EVIO composite type data
* between IEEE (big endian) and DECS (little endian). This
@@ -1475,8 +1647,10 @@
* @param ifmt format list as produced by {@link #compositeFormatToInt(String)}
* @param srcOrder byte order of the src data array
*
- * @throws EvioException if src == null; if nBytes or nfmt args < 0;
- * if src = dest and offsets are not the same
+ * @throws EvioException if src == null or ifmt == null;
+ * if nBytes or ifmt size <= 0;
+ * if src = dest and offsets are not the same;
+ * if src or dest is too small
*/
public static void swapData(byte[] src, int srcOff, byte[] dest, int destOff,
int nBytes, List<Integer> ifmt, ByteOrder srcOrder)
@@ -1523,8 +1697,8 @@
* @param nBytes length of data array in bytes
* @param ifmt format list as produced by {@link #compositeFormatToInt(String)}
*
- * @throws EvioException if nBytes or nfmt args < 0 ; if srcBuf or destBuf is too
- * small
+ * @throws EvioException if ifmt null; ifmt size or nBytes <= 0;
+ * srcBuf or destBuf is too small
*/
public static void swapData(ByteBuffer srcBuf, ByteBuffer destBuf,
int nBytes, List<Integer> ifmt)
@@ -1532,8 +1706,6 @@
boolean debug = false;
int imt, ncnf, kcnf, lev, iterm;
- // size of int list
- int nfmt = ifmt.size();
class LV {
int left; // index of ifmt[] element containing left parenthesis "("
@@ -1543,7 +1715,11 @@
};
// check args
- if (nBytes <= 0 || nfmt <= 0) throw new EvioException("bad argument value(s)");
+ if (ifmt == null || nBytes <= 0) throw new EvioException("bad argument value(s)");
+
+ // size of int list
+ int nfmt = ifmt.size();
+ if (nfmt <= 0) throw new EvioException("empty format list");
if (destBuf == null) destBuf = srcBuf;
boolean inPlace = (srcBuf == destBuf);
@@ -1805,8 +1981,8 @@
* @param data data to convert to raw bytes
* @param ifmt format list as produced by {@link #compositeFormatToInt(String)}
*
- * @throws EvioException if nBytes or nfmt args < 0 ; if srcBuf or destBuf is too
- * small
+ * @throws EvioException if ifmt size <= 0; if srcBuf or destBuf is too
+ * small; not enough dataItems for the given format
*/
public static void dataToRawBytes(ByteBuffer rawBuf, CompositeData.Data data,
List<Integer> ifmt)
@@ -1826,7 +2002,6 @@
if (ifmt == null || data == null || rawBuf == null) throw new EvioException("arg is null");
// size of format list
- // TODO: bug, ifmt may be NULLLLLL in other METHODS ......
int nfmt = ifmt.size();
if (nfmt <= 0) throw new EvioException("empty format list");
@@ -1840,8 +2015,6 @@
lev = 0; // parenthesis level
iterm = 0;
- ListIterator<Integer> nListIter = data.nList.listIterator();
-
int itemIndex = 0;
int itemCount = data.dataItems.size();
@@ -1890,10 +2063,10 @@
kcnf = 0;
// get "N" value from List
- if (!nListIter.hasNext()) {
- throw new EvioException("Not enough N values provided");
+ if (data.dataTypes.get(itemIndex) != DataType.INT32) {
+ throw new EvioException("Data type mismatch");
}
- ncnf = nListIter.next();
+ ncnf = (Integer)data.dataItems.get(itemIndex++);
if (debug) System.out.println("N 1 from list = " + ncnf);
// put into buffer (relative put)
@@ -1936,10 +2109,10 @@
// if 'ncnf' is zero, get "N" from list (always in 'int' format)
if (ncnf == 0) {
// get "N" value from List
- if (!nListIter.hasNext()) {
- throw new EvioException("Not enough N values provided");
+ if (data.dataTypes.get(itemIndex) != DataType.INT32) {
+ throw new EvioException("Data type mismatch");
}
- ncnf = nListIter.next();
+ ncnf = (Integer)data.dataItems.get(itemIndex++);
// put into buffer (relative put)
rawBuf.putInt(ncnf);
@@ -1954,6 +2127,7 @@
// ncnf - how many times format repeated
// Convert data type kcnf
+if (debug) System.out.println("Convert data of type = " + kcnf + ", itemIndex = " + itemIndex);
switch (kcnf) {
// 64 Bits
case 8:
@@ -2090,7 +2264,7 @@
*/
public void process() {
- boolean debug=false;
+ boolean debug = false;
int imt, ncnf, kcnf, lev, iterm;
// size of int list
@@ -2412,6 +2586,9 @@
case FLOAT32:
sb.append("F="); sb.append(getFloat());
break;
+ case NVALUE:
+ sb.append("N="); sb.append(getNValue());
+ break;
case CHARSTAR8:
sb.append("a=");
String[] strs = getStrings();
@@ -2941,9 +3118,9 @@
/**
* This method returns a string representation of this CompositeData object
- * suitable for displaying in EventTreeFrame gui. Each data item is separated
- * from those before and after by a line. Non-parenthesis repeats are printed
- * together.
+ * suitable for displaying in {@link org.jlab.coda.jevio.graphics.EventTreeFrame} gui.
+ * Each data item is separated from those before and after by a line.
+ * Non-parenthesis repeats are printed together.
*
* @param hex if <code>true</code> then print integers in hexadecimal
*/
jevio-base/src/main/java/org/jlab/coda/jevio
diff -u -r1.3 -r1.4
--- EvioDictionaryEntry.java 28 Feb 2012 19:41:36 -0000 1.3
+++ EvioDictionaryEntry.java 3 Apr 2013 20:07:46 -0000 1.4
@@ -6,6 +6,7 @@
* An entry into the evio dictionary.
*
* @author heddle
+ * @deprecated
*
*/
public class EvioDictionaryEntry implements Comparable<EvioDictionaryEntry> {
jevio-base/src/main/java/org/jlab/coda/jevio
diff -u -r1.1 -r1.2
--- BlockHeaderV2.java 28 Feb 2012 19:41:36 -0000 1.1
+++ BlockHeaderV2.java 3 Apr 2013 20:07:46 -0000 1.2
@@ -167,6 +167,31 @@
/**
+ * This copy constructor creates an evio version 1-3 BlockHeader
+ * from another object of this class.
+ * @param blkHeader block header object to copy
+ */
+ public BlockHeaderV2(BlockHeaderV2 blkHeader) {
+ if (blkHeader == null) {
+ return;
+ }
+ size = blkHeader.size;
+ number = blkHeader.number;
+ headerLength = blkHeader.headerLength;
+ version = blkHeader.version;
+ end = blkHeader.end;
+ start = blkHeader.start;
+ reserved1 = blkHeader.reserved1;
+ magicNumber = blkHeader.magicNumber;
+ bufferStartingPosition = blkHeader.bufferStartingPosition;
+ }
+
+ /*** {@inheritDoc} */
+ public Object clone() {
+ return new BlockHeaderV2(this);
+ }
+
+ /**
* Gets whether this block's first event is an evio dictionary.
* This is not implemented in evio versions 1-3. Just return false.
*
@@ -460,7 +485,8 @@
/**
* Gives the bytes remaining in this block (physical record) given a buffer position. The position is an absolute
* position in a byte buffer. This assumes that the absolute position in <code>bufferStartingPosition</code> is
- * being maintained properly by the reader.
+ * being maintained properly by the reader. No block is longer than 2.1GB - 31 bits of length. This is for
+ * practical reasons - so a block can be read into a single byte array.
*
* @param position the absolute current position is a byte buffer.
* @return the number of bytes remaining in this block (physical record.)
@@ -477,7 +503,7 @@
throw new EvioException("Provided position beyond buffer end position.");
}
- return nextBufferStart - position;
+ return (int)(nextBufferStart - position);
}
/**
jevio-base/src/main/java/org/jlab/coda/jevio
diff -u -r1.4 -r1.5
--- EventWriter.java 20 Mar 2012 23:21:49 -0000 1.4
+++ EventWriter.java 3 Apr 2013 20:07:46 -0000 1.5
@@ -1,11 +1,10 @@
package org.jlab.coda.jevio;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
+import java.io.*;
+import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
+import java.nio.channels.FileChannel;
import java.util.LinkedList;
import java.util.BitSet;
@@ -20,39 +19,94 @@
public class EventWriter {
/**
- * Offset to where the block length is written in the byte buffer, which always has a physical
- * record header at the top.
+ * This <code>enum</code> denotes the status of a read. <br>
+ * SUCCESS indicates a successful read/write. <br>
+ * END_OF_FILE indicates that we cannot read because an END_OF_FILE has occurred. Technically this means that what
+ * ever we are trying to read is larger than the buffer's unread bytes.<br>
+ * EVIO_EXCEPTION indicates that an EvioException was thrown during a read, possibly due to out of range values,
+ * such as a negative start position.<br>
+ * CANNOT_OPEN_FILE that we cannot write because the destination file cannot be opened.<br>
+ * UNKNOWN_ERROR indicates that an unrecoverable error has occurred.
+ */
+ public static enum IOStatus {
+ SUCCESS, END_OF_FILE, EVIO_EXCEPTION, CANNOT_OPEN_FILE, UNKNOWN_ERROR
+ }
+
+
+ /**
+ * Offset to where the block length is written in the byte buffer,
+ * which always has a physical record header at the top.
*/
private static int BLOCK_LENGTH_OFFSET = 0;
/**
- * Offset to where the header length is written in the byte buffer, which always has a physical
- * record header at the top.
+ * Offset to where the block number is written in the byte buffer,
+ * which always has a physical record header at the top.
+ */
+ private static int BLOCK_NUMBER_OFFSET = 4;
+
+ /**
+ * Offset to where the header length is written in the byte buffer,
+ * which always has a physical record header at the top.
*/
private static int HEADER_LENGTH_OFFSET = 8;
/**
- * Offset to where the event count is written in the byte buffer, which always has a physical
- * record header at the top.
+ * Offset to where the event count is written in the byte buffer,
+ * which always has a physical record header at the top.
*/
private static int EVENT_COUNT_OFFSET = 12;
/**
- * Offset to where the bit information is written in the byte buffer, which always has a physical
- * record header at the top.
+ * Offset to where the first reserved workd is written in the byte buffer,
+ * which always has a physical record header at the top.
+ */
+ private static int RESERVED1_COUNT_OFFSET = 16;
+
+ /**
+ * Offset to where the bit information is written in the byte buffer,
+ * which always has a physical record header at the top.
*/
private static int BIT_INFO_OFFSET = 20;
- /**
- * The default maximum size for a single block used for writing, in ints (2^18).
- * This gives block sizes of 1,048,576 bytes (2^20) or about 1MB.
- */
- private static int DEFAULT_BLOCK_SIZE_LIMIT = 262144;
+ /**
+ * Offset to where the magic number is written in the byte buffer,
+ * which always has a physical record header at the top.
+ */
+ private static final int MAGIC_OFFSET = 28;
+
+ /** Mask to get version number from 6th int in block. */
+ private static final int VERSION_MASK = 0xff;
/**
- * The default maximum event count for a single block used for writing.
+ * The default maximum size for a single block used for writing, in ints.
+ * This gives block sizes of about 4MB. It is a soft limit since a single
+ * event larger than this limit may need to be written.
*/
- private static int DEFAULT_BLOCK_COUNT_LIMIT = 200;
+ private static int DEFAULT_BLOCK_SIZE = 1024000;
+
+ /** The default maximum event count for a single block used for writing. */
+ private static int DEFAULT_BLOCK_COUNT = 10000;
+
+ /**
+ * The upper limit of maximum size for a single block used for writing,
+ * in ints. This gives block sizes of about 40MB. It is a soft limit since
+ * a single event larger than this limit may need to be written.
+ */
+ private static int MAX_BLOCK_SIZE = 10240000;
+
+ /** The upper limit of maximum event count for a single block used for writing. */
+ private static int MAX_BLOCK_COUNT = 100000;
+
+ /**
+ * The lower limit of maximum size for a single block used for writing,
+ * in ints. This gives block sizes of about 400 bytes. It is a soft limit since
+ * a single event larger than this limit may need to be written.
+ */
+ private static int MIN_BLOCK_SIZE = 100;
+
+ /** The lower limit of maximum event count for a single block used for writing. */
+ private static int MIN_BLOCK_COUNT = 1;
@@ -60,20 +114,18 @@
* Maximum block size for a single block (block header & following events).
* There may be exceptions to this limit if the size of an individual
* event exceeds this limit.
- * Default is DEFAULT_BLOCK_SIZE_LIMIT.
+ * Default is {@link #DEFAULT_BLOCK_SIZE}.
*/
private int blockSizeMax;
/**
* Maximum number of events in a block (events following a block header).
- * Default is DEFAULT_BLOCK_COUNT_LIMIT.
+ * Default is {@link #DEFAULT_BLOCK_COUNT}.
*/
private int blockCountMax;
- /**
- * Running count of the block number.
- */
- private int blockNumber = 0;
+ /** Running count of the block number. */
+ private int blockNumber;
/**
* Size in bytes needed to write all the events in the <code>eventBufferHoldingList</code>
@@ -87,9 +139,7 @@
*/
private String xmlDictionary;
- /**
- * The next write will include an xml dictionary.
- */
+ /** The next write will include an xml dictionary. */
private boolean writeDictionary;
/**
@@ -101,39 +151,46 @@
*/
private BitSet bitInfo;
- /**
- * <code>True</code> if {@link #close()} was called, else <code>false</code>.
- */
+ /** <code>True</code> if {@link #close()} was called, else <code>false</code>. */
private boolean closed;
- /**
- * <code>True</code> if writing to file, else <code>false</code>.
- */
+ /** <code>True</code> if writing to file, else <code>false</code>. */
private boolean toFile;
- /**
- * Version 4 block header reserved int 1. Used by CODA for source ID in event building.
- */
+ /** <code>True</code> if appending to file/buffer, <code>false</code> if (over)writing. */
+ private boolean append;
+
+ /** <code>True</code> if a last, empty block header has already been written. */
+ private boolean lastBlockOut;
+
+ /** Version 4 block header reserved int 1. Used by CODA for source ID in event building. */
private int reserved1;
- /**
- * Version 4 block header reserved int 2.
- */
+ /** Version 4 block header reserved int 2. */
private int reserved2;
+ /** Keep tabs on where we initially started. For files this is 0. */
+ private int initialPosition;
+
+ /** Number of events written to buffer or file (although may not be flushed yet). */
+ private int eventsWritten;
+
+ /** Contains an empty, last block header which is placed at the file/buffer end
+ * after each write. */
+ private ByteBuffer emptyLastHeader;
+
//-----------------------
// File related members
//-----------------------
- /**
- * The byte order in which to write a file.
- */
+ /** The byte order in which to write a file. */
private ByteOrder byteOrder;
- /**
- * The output stream used for writing a file.
- */
- private FileOutputStream fileOutputStream;
+ /** The output stream used for writing a file. */
+ private FileOutputStream fileOutputStream;
+
+ /** The file channel, used for writing a file, derived from the output stream. */
+ private FileChannel fileChannel;
/**
* This is an internal byte buffer corresponding to one block (physical record). When this gets full it
@@ -153,19 +210,12 @@
// Buffer related members
//-----------------------
- /**
- * The output buffer when writing to a buffer.
- */
+ /** The output buffer when writing to a buffer. */
private ByteBuffer buffer;
- /**
- * When writing to a buffer, keep tabs on the front of the last header written.
- */
+ /** When writing to a buffer, keep tabs on the front of the last header written. */
private int bufferHeaderPosition;
- /** Is this the first write into a buffer? */
- private boolean firstWrite = true;
-
/**
* List used to store events (in EvioBank form) temporarily before writing them to a buffer.
* This is done to accumulate events while keeping track of the total memory needed to write them.
@@ -176,131 +226,241 @@
+ //---------------------------------------------
+ // FILE Constructors
+ //---------------------------------------------
/**
- * Creates an event writer for writing to a file in native byte order.
- * WARNING: If the file already exists, an attempt will be made to overwrite it.
- * Appending to an existing event file is not supported.
- *
- * @param file the file to write to. Will be created.<br>
- * WARNING: If the file already exists, an attempt will be made to overwrite it.
- * Appending to an existing event file is not supported.
+ * Creates an <code>EventWriter</code> for writing to a file in native byte order.
+ * If the file already exists, its contents will be overwritten.
+ * If it doesn't exist, it will be created.
+ *
+ * @param file the file object to write to.<br>
* @throws EvioException block size too small or file cannot be created
- */
+ */
public EventWriter(File file) throws EvioException {
- this(file, DEFAULT_BLOCK_SIZE_LIMIT, DEFAULT_BLOCK_COUNT_LIMIT,
- ByteOrder.nativeOrder(), null, null);
+ this(file, false);
+ }
+
+ /**
+ * Creates an <code>EventWriter</code> for writing to a file in native byte order.
+ * If the file already exists, its contents will be overwritten unless
+ * it is being appended to. If it doesn't exist, it will be created.
+ *
+ * @param file the file object to write to.<br>
+ * @param append if <code>true</code> and the file already exists,
+ * all events to be written will be appended to the
+ * end of the file.
+ *
+ * @throws EvioException block size too small or file cannot be created
+ */
+ public EventWriter(File file, boolean append) throws EvioException {
+ this(file, DEFAULT_BLOCK_SIZE, DEFAULT_BLOCK_COUNT,
+ ByteOrder.nativeOrder(), null, null, true, append);
}
/**
- * Creates an event writer for writing to a file in native byte order.
- * WARNING: If the file already exists, an attempt will be made to overwrite it.
- * Appending to an existing event file is not supported.
+ * Creates an <code>EventWriter</code> for writing to a file in native byte order.
+ * If the file already exists, its contents will be overwritten unless
+ * it is being appended to. If it doesn't exist, it will be created.
+ *
+ * @param file the file object to write to.<br>
+ * @param dictionary dictionary in xml format or null if none.
+ * @param append if <code>true</code> and the file already exists,
+ * all events to be written will be appended to the
+ * end of the file.
*
- * @param filename the file to write to. Will be created.<br>
- * WARNING: If the file already exists, an attempt will be made to overwrite it.
- * Appending to an existing event file is not supported.
* @throws EvioException block size too small or file cannot be created
- */
+ */
+ public EventWriter(File file, String dictionary, boolean append) throws EvioException {
+ this(file, DEFAULT_BLOCK_SIZE, DEFAULT_BLOCK_COUNT,
+ ByteOrder.nativeOrder(), dictionary, null, true, append);
+ }
+
+ /**
+ * Creates an <code>EventWriter</code> for writing to a file in native byte order.
+ * If the file already exists, its contents will be overwritten.
+ * If it doesn't exist, it will be created.
+ *
+ * @param filename name of the file to write to.<br>
+ * @throws EvioException block size too small or file cannot be created
+ */
public EventWriter(String filename) throws EvioException {
- this(new File(filename), DEFAULT_BLOCK_SIZE_LIMIT, DEFAULT_BLOCK_COUNT_LIMIT,
- ByteOrder.nativeOrder(), null, null);
+ this(filename, false);
}
/**
- * Create an <code>EventWriter</code> for writing events to a file.
- * WARNING: If the file already exists, an attempt will be made to overwrite it.
- * Appending to an existing event file is not supported.
+ * Creates an <code>EventWriter</code> for writing to a file in native byte order.
+ * If the file already exists, its contents will be overwritten unless
+ * it is being appended to. If it doesn't exist, it will be created.
*
+ * @param filename name of the file to write to.<br>
+ * @param append if <code>true</code> and the file already exists,
+ * all events to be written will be appended to the
+ * end of the file.
*
- * @param file the file to write to. Will be created.<br>
- * WARNING: If the file already exists, an attempt will be made to overwrite it.
- * Appending to an existing event file is not supported.
- * @param blockSizeMax the max blocksize to use which must be >= 1K and <= 1M ints.
- * The size of the block will not be larger than this size.
- * The one exception is when a single event being written is larger
- * than the max size.
- * @param blockCountMax the max number of events in a single block which must be
- * >= 1 and <= 1000.
+ * @throws EvioException block size too small or file cannot be created
+ */
+ public EventWriter(String filename, boolean append) throws EvioException {
+ this(new File(filename), DEFAULT_BLOCK_SIZE, DEFAULT_BLOCK_COUNT,
+ ByteOrder.nativeOrder(), null, null, true, append);
+ }
+
+ /**
+ * Creates an <code>EventWriter</code> for writing to a file in the
+ * specified byte order.
+ * If the file already exists, its contents will be overwritten unless
+ * it is being appended to. If it doesn't exist, it will be created.
+ *
+ * @param filename name of the file to write to.<br>
+ * @param append if <code>true</code> and the file already exists,
+ * all events to be written will be appended to the
+ * end of the file.
* @param byteOrder the byte order in which to write the file.
+ *
+ * @throws EvioException block size too small or file cannot be created
+ */
+ public EventWriter(String filename, boolean append, ByteOrder byteOrder) throws EvioException {
+ this(new File(filename), DEFAULT_BLOCK_SIZE, DEFAULT_BLOCK_COUNT,
+ byteOrder, null, null, true, append);
+ }
+
+ /**
+ * Create an <code>EventWriter</code> for writing events to a file.
+ * If the file already exists, its contents will be overwritten.
+ * If it doesn't exist, it will be created.
+ *
+ * @param file the file object to write to.<br>
+ * @param blockSizeMax the max blocksize to use which must be >= {@link #MIN_BLOCK_SIZE}
+ * and <= {@link #MAX_BLOCK_SIZE} ints.
+ * The size of the block will not be larger than this size
+ * unless a single event itself is larger.
+ * @param blockCountMax the max number of events in a single block which must be
+ * >= {@link #MIN_BLOCK_COUNT} and <= {@link #MAX_BLOCK_COUNT}.
+ * @param byteOrder the byte order in which to write the file.
* @param xmlDictionary dictionary in xml format or null if none.
- * @param bitInfo set of bits to include in first block header.
+ * @param bitInfo set of bits to include in first block header.
+ *
* @throws EvioException block size too small or file cannot be created
*/
public EventWriter(File file, int blockSizeMax, int blockCountMax, ByteOrder byteOrder,
- String xmlDictionary, BitSet bitInfo) throws EvioException {
+ String xmlDictionary, BitSet bitInfo)
+ throws EvioException {
- if (blockSizeMax < 100 || blockSizeMax > 1000000) {
- throw new EvioException("Pick a block size >= 1K and <= 1M");
- }
+ this(file, blockSizeMax, blockCountMax, byteOrder,
+ xmlDictionary, bitInfo, true, false);
+ }
- if (blockCountMax < 1 || blockCountMax > 1000) {
- throw new EvioException("Pick a block count >= 1 and <= 1000");
- }
+ /**
+ * Create an <code>EventWriter</code> for writing events to a file.
+ * If the file already exists, its contents will be overwritten
+ * unless the "overWriteOK" argument is <code>false</code> in
+ * which case an exception will be thrown. If it doesn't exist,
+ * it will be created.
+ *
+ * @param file the file to write to.<br>
+ * @param blockSizeMax the max blocksize to use which must be >= {@link #MIN_BLOCK_SIZE}
+ * and <= {@link #MAX_BLOCK_SIZE} ints.
+ * The size of the block will not be larger than this size
+ * unless a single event itself is larger.
+ * @param blockCountMax the max number of events in a single block which must be
+ * >= {@link #MIN_BLOCK_COUNT} and <= {@link #MAX_BLOCK_COUNT}.
+ * @param byteOrder the byte order in which to write the file.
+ * @param xmlDictionary dictionary in xml format or null if none.
+ * @param bitInfo set of bits to include in first block header.
+ * @param overWriteOK if <code>false</code> and the file already exists,
+ * an exception is thrown rather than overwriting it.
+ *
+ * @throws EvioException block size too small, file exists and cannot be deleted,
+ * file exists and user requested no deletion.
+ */
+ public EventWriter(File file, int blockSizeMax, int blockCountMax, ByteOrder byteOrder,
+ String xmlDictionary, BitSet bitInfo, boolean overWriteOK)
+ throws EvioException {
- toFile = true;
- this.blockSizeMax = blockSizeMax;
- this.blockCountMax = blockCountMax;
- this.byteOrder = byteOrder;
- this.xmlDictionary = xmlDictionary;
+ this(file, blockSizeMax, blockCountMax, byteOrder,
+ xmlDictionary, bitInfo, overWriteOK, false);
+ }
- if (bitInfo != null) {
- this.bitInfo = (BitSet)bitInfo.clone();
- }
- else {
- this.bitInfo = new BitSet(24);
- }
- if (xmlDictionary != null) {
- this.bitInfo.set(0,true);
- }
-
- try {
- fileOutputStream = new FileOutputStream(file);
- }
- catch (FileNotFoundException e) {
- throw new EvioException(e.getMessage());
- }
- }
/**
* Create an <code>EventWriter</code> for writing events to a file.
+ * If the file already exists, its contents will be overwritten
+ * unless the "overWriteOK" argument is <code>false</code> in
+ * which case an exception will be thrown. Unless ..., the option to
+ * append these events to an existing file is <code>true</code>,
+ * in which case everything is fine. If the file doesn't exist,
+ * it will be created. Byte order defaults to big endian if arg is null.
*
- * @param file the file to write to. Will be created.<br>
- * WARNING: If the file already exists, an attempt will be made to delete it. Appending
- * to an existing event file is not supported.
- * @param blockSizeMax the max blocksize to use which must be >= 1K and <= 1M ints.
- * The size of the block will not be larger than this size.
- * The one exception is when a single event being written is larger
- * than the max size.
+ * @param file the file to write to.<br>
+ * @param blockSizeMax the max blocksize to use which must be >= {@link #MIN_BLOCK_SIZE}
+ * and <= {@link #MAX_BLOCK_SIZE} ints.
+ * The size of the block will not be larger than this size
+ * unless a single event itself is larger.
* @param blockCountMax the max number of events in a single block which must be
- * >= 1 and <= 1000.
- * @param byteOrder the byte order in which to write the file.
+ * >= {@link #MIN_BLOCK_COUNT} and <= {@link #MAX_BLOCK_COUNT}.
+ * @param byteOrder the byte order in which to write the file. This is ignored
+ * if appending to existing file.
* @param xmlDictionary dictionary in xml format or null if none.
- * @param bitInfo set of bits to include in first block header.
- * @param okToDelete if <code>true</code> and the file already exists,
- * an attempt will be made to delete the existing file.
- * Appending to an existing event file is not supported at this time.
- * @throws EvioException block size too small, file exists and cannot be deleted,
- * file exists and user requested no deletion.
+ * @param bitInfo set of bits to include in first block header.
+ * @param overWriteOK if <code>false</code> and the file already exists,
+ * an exception is thrown rather than overwriting it.
+ * @param append if <code>true</code> and the file already exists,
+ * all events to be written will be appended to the
+ * end of the file.
+ *
+ * @throws EvioException if blockSizeMax or blockCountMax exceed limits;
+ * if defined dictionary while appending;
+ * if file arg is null;
+ * if file could not be opened or positioned;
+ * if file exists but user requested no over-writing or appending.
+ *
*/
public EventWriter(File file, int blockSizeMax, int blockCountMax, ByteOrder byteOrder,
- String xmlDictionary, BitSet bitInfo, boolean okToDelete) throws EvioException {
+ String xmlDictionary, BitSet bitInfo, boolean overWriteOK,
+ boolean append) throws EvioException {
+
+ if (blockSizeMax < MIN_BLOCK_SIZE || blockSizeMax > MAX_BLOCK_SIZE) {
+ throw new EvioException("Block size arg must be bigger or smaller");
+ }
+
+ if (blockCountMax < MIN_BLOCK_COUNT || blockCountMax > MAX_BLOCK_COUNT) {
+ throw new EvioException("Block count arg must be bigger or smaller");
+ }
- if (blockSizeMax < 100 || blockSizeMax > 1000000) { // TODO: restore limits
- throw new EvioException("Pick a block size >= 1K and <= 1M");
+ if (file == null) {
+ throw new EvioException("Null file arg");
}
- if (blockCountMax < 1 || blockCountMax > 1000) {
- throw new EvioException("Pick a block count >= 1 and <= 1000");
+ if (byteOrder == null) {
+ byteOrder = ByteOrder.BIG_ENDIAN;
}
- toFile = true;
- this.blockSizeMax = blockSizeMax;
+ toFile = true;
+ this.append = append;
+ // byte order may be overwritten if appending
+ this.byteOrder = byteOrder;
+ this.blockSizeMax = blockSizeMax;
this.blockCountMax = blockCountMax;
- this.byteOrder = byteOrder;
this.xmlDictionary = xmlDictionary;
+ blockNumber = 1;
+
+ // For convenience, create buffer to hold a last, empty block header.
+ emptyLastHeader = ByteBuffer.allocate(32);
+ emptyLastHeader.order(byteOrder);
+ emptyLastHeader.putInt(8); // block len, words
+ emptyLastHeader.putInt(1); // block number
+ emptyLastHeader.putInt(8); // header len, words
+ emptyLastHeader.putInt(0); // event count
+ emptyLastHeader.putInt(0); // reserved 1
+ emptyLastHeader.putInt(0x204); // last block = true, version = 4
+ emptyLastHeader.putInt(0); // reserved 2
+ emptyLastHeader.putInt(0xc0da0100); // magic #
+ emptyLastHeader.flip();
+
+
if (bitInfo != null) {
this.bitInfo = (BitSet)bitInfo.clone();
}
@@ -309,124 +469,293 @@
}
if (xmlDictionary != null) {
+ // Appending means not adding new dictionary
+ if (append) {
+ throw new EvioException("Cannot specify dictionary when appending");
+ }
this.bitInfo.set(0,true);
+ writeDictionary = true;
}
- if (file == null) {
- throw new EvioException("Null file in EventWriter constructor");
- }
-
- // handle existing file
- if (file.exists() && file.isFile()) {
- if (okToDelete) {
- boolean deleted = file.delete();
- if (!deleted) {
- throw new EvioException("File in EventWriter constructor already exists, and could not be deleted. \n" +
- "File: " + file.getPath());
- }
- }
- else { // user requested no delete
- throw new EvioException("File in EventWriter constructor already exists, and user requested no deletion. \n" +
- "File: " + file.getPath());
- }
+ // If we can't overwrite or append and file exists, throw exception
+ if (!overWriteOK && !append && (file.exists() && file.isFile())) {
+ throw new EvioException("File exists but user requested no over-writing or appending, "
+ + file.getPath());
}
try {
- fileOutputStream = new FileOutputStream(file);
- }
- catch (FileNotFoundException e) {
- throw new EvioException("For whatever reason, the file could not be opened. \n " +
- "It could mean there is no permission to open the file for writing \n" +
- "or that the file was a directory." +
- "File: " + file.getPath());
+ if (append) {
+ RandomAccessFile raf = new RandomAccessFile(file, "rw");
+ fileChannel = raf.getChannel();
+
+ // Something to read block headers into
+ buffer = ByteBuffer.allocate(32);
+
+ // Look at first block header to find endianness & version.
+ // Endianness given in constructor arg, when appending, is ignored.
+ examineFirstBufferHeader();
+
+ // Oops, gotta redo this since file has different byte order
+ // than specified in constructor arg.
+ if (this.byteOrder != byteOrder) {
+ emptyLastHeader.clear();
+ emptyLastHeader.order(this.byteOrder);
+ emptyLastHeader.putInt(8); // block len, words
+ emptyLastHeader.putInt(1); // block number
+ emptyLastHeader.putInt(8); // header len, words
+ emptyLastHeader.putInt(0); // event count
+ emptyLastHeader.putInt(0); // reserved 1
+ emptyLastHeader.putInt(0x204); // last block = true, version = 4
+ emptyLastHeader.putInt(0); // reserved 2
+ emptyLastHeader.putInt(0xc0da0100); // magic #
+ emptyLastHeader.flip();
+ }
+
+ // Prepare for appending by moving file position
+ toAppendPosition();
+
+ // Create block buffer with default size.
+ // This method will be called before each write.
+ // Do this after examineFirstBufferHeader() so the
+ // byte order is set properly. If appending, don't
+ // increase the blockNumber since it was properly
+ // set in toAppendPosition().
+ getCleanBuffer(0,0,false);
+ }
+ else {
+ fileOutputStream = new FileOutputStream(file, append);
+ fileChannel = fileOutputStream.getChannel();
+
+ // Create block buffer w/ default size & set blockNumber to 1
+ getCleanBuffer(0,0,true);
+ }
}
+ catch (FileNotFoundException e) {
+ throw new EvioException("File could not be opened for writing, " +
+ file.getPath(), e);
+ }
+ catch (IOException e) {
+ throw new EvioException("File could not be positioned for appending, " +
+ file.getPath(), e);
+ }
+
}
+
+ //---------------------------------------------
+ // BUFFER Constructors
+ //---------------------------------------------
+
/**
* Create an <code>EventWriter</code> for writing events to a ByteBuffer.
+ * Uses the default number and size of blocks in buffer. Will overwrite
+ * any existing data in buffer!
*
- * @param buf the buffer to write to.
- * @param blockSizeMax the max blocksize to use which must be >= 1K and <= 1M ints.
- * The size of the block will not be larger than this size.
- * The one exception is when a single event being written is larger
- * than the max size.
- * @param blockCountMax the max number of events in a single block which must be
- * >= 1 and <= 1000.
- * @param xmlDictionary dictionary in xml format or null if none.
- * @param bitInfo set of bits to include in first block header.
+ * @param buf the buffer to write to.
+ * @throws EvioException if buf arg is null
+ */
+ public EventWriter(ByteBuffer buf) throws EvioException {
+
+ this(buf, DEFAULT_BLOCK_SIZE, DEFAULT_BLOCK_COUNT, null, null, 0, false);
+ }
+
+
+ /**
+ * Create an <code>EventWriter</code> for writing events to a ByteBuffer.
+ * Uses the default number and size of blocks in buffer.
+ *
+ * @param buf the buffer to write to.
+ * @param append if <code>true</code>, all events to be written will be
+ * appended to the end of the buffer.
+ * @throws EvioException if buf arg is null
+ */
+ public EventWriter(ByteBuffer buf, boolean append) throws EvioException {
+
+ this(buf, DEFAULT_BLOCK_SIZE, DEFAULT_BLOCK_COUNT, null, null, 0, append);
+ }
+
+
+ /**
+ * Create an <code>EventWriter</code> for writing events to a ByteBuffer.
+ * Uses the default number and size of blocks in buffer.
+ *
+ * @param buf the buffer to write to.
+ * @param xmlDictionary dictionary in xml format or null if none.
+ * @param append if <code>true</code>, all events to be written will be
+ * appended to the end of the buffer.
+ * @throws EvioException if buf arg is null
+ */
+ public EventWriter(ByteBuffer buf, String xmlDictionary, boolean append) throws EvioException {
+
+ this(buf, DEFAULT_BLOCK_SIZE, DEFAULT_BLOCK_COUNT, xmlDictionary, null, 0, append);
+ }
+
+
+ /**
+ * Create an <code>EventWriter</code> for writing events to a ByteBuffer.
+ *
+ * @param buf the buffer to write to.
+ * @param blockSizeMax the max blocksize to use which must be >= {@link #MIN_BLOCK_SIZE}
+ * and <= {@link #MAX_BLOCK_SIZE} ints.
+ * The size of the block will not be larger than this size
+ * unless a single event itself is larger.
+ * @param blockCountMax the max number of events in a single block which must be
+ * >= {@link #MIN_BLOCK_COUNT} and <= {@link #MAX_BLOCK_COUNT}.
+ * @param xmlDictionary dictionary in xml format or null if none.
+ * @param bitInfo set of bits to include in first block header.
* @throws EvioException if blockSizeMax or blockCountMax exceed limits; if buf arg is null
*/
public EventWriter(ByteBuffer buf, int blockSizeMax, int blockCountMax,
String xmlDictionary, BitSet bitInfo) throws EvioException {
- if (blockSizeMax < 100 || blockSizeMax > 1000000) { // TODO: restore limits
- throw new EvioException("Pick a block size >= 1K and <= 1M");
- }
+ this(buf, blockSizeMax, blockCountMax, xmlDictionary, bitInfo, 0, false);
+ }
- if (blockCountMax < 1 || blockCountMax > 1000) {
- throw new EvioException("Pick a block count >= 1 and <= 1000");
- }
- if (buf == null) {
- throw new EvioException("Buffer arg cannot be null");
- }
-
- toFile = false;
- this.buffer = buf;
- this.blockSizeMax = blockSizeMax;
- this.blockCountMax = blockCountMax;
- this.xmlDictionary = xmlDictionary;
+ /**
+ * Create an <code>EventWriter</code> for writing events to a ByteBuffer.
+ *
+ * @param buf the buffer to write to.
+ * @param blockSizeMax the max blocksize to use which must be >= {@link #MIN_BLOCK_SIZE}
+ * and <= {@link #MAX_BLOCK_SIZE} ints.
+ * The size of the block will not be larger than this size
+ * unless a single event itself is larger.
+ * @param blockCountMax the max number of events in a single block which must be
+ * >= {@link #MIN_BLOCK_COUNT} and <= {@link #MAX_BLOCK_COUNT}.
+ * @param xmlDictionary dictionary in xml format or null if none.
+ * @param bitInfo set of bits to include in first block header.
+ * @param append if <code>true</code>, all events to be written will be
+ * appended to the end of the buffer.
+ *
+ * @throws EvioException if blockSizeMax or blockCountMax exceed limits;
+ * if buf arg is null;
+ * if defined dictionary while appending;
+ */
+ public EventWriter(ByteBuffer buf, int blockSizeMax, int blockCountMax,
+ String xmlDictionary, BitSet bitInfo,
+ boolean append) throws EvioException {
- if (bitInfo != null) {
- this.bitInfo = (BitSet)bitInfo.clone();
- }
- else {
- this.bitInfo = new BitSet(24);
- }
-
- if (xmlDictionary != null) {
- this.bitInfo.set(0,true);
- }
+ this(buf, blockSizeMax, blockCountMax, xmlDictionary, bitInfo, 0, append);
}
/**
* Create an <code>EventWriter</code> for writing events to a ByteBuffer.
*
- * @param buf the buffer to write to.
- * @param blockSizeMax the max blocksize to use which must be >= 1K and <= 1M ints.
- * The size of the block will not be larger than this size.
- * The one exception is when a single event being written is larger
- * than the max size.
- * @param blockCountMax the max number of events in a single block which must be
- * >= 1 and <= 1000.
- * @param xmlDictionary dictionary in xml format or null if none.
- * @param bitInfo set of bits to include in first block header.
- * @param reserved1 set the value of the first "reserved" int in first block header.
- * NOTE: only CODA (i.e. EMU) software should use this.
+ * @param buf the buffer to write to.
+ * @param blockSizeMax the max blocksize to use which must be >= {@link #MIN_BLOCK_SIZE}
+ * and <= {@link #MAX_BLOCK_SIZE} ints.
+ * The size of the block will not be larger than this size
+ * unless a single event itself is larger.
+ * @param blockCountMax the max number of events in a single block which must be
+ * >= {@link #MIN_BLOCK_COUNT} and <= {@link #MAX_BLOCK_COUNT}.
+ * @param xmlDictionary dictionary in xml format or null if none.
+ * @param bitInfo set of bits to include in first block header.
+ * @param reserved1 set the value of the first "reserved" int in first block header.
+ * NOTE: only CODA (i.e. EMU) software should use this.
* @throws EvioException if blockSizeMax or blockCountMax exceed limits; if buf arg is null
*/
public EventWriter(ByteBuffer buf, int blockSizeMax, int blockCountMax,
String xmlDictionary, BitSet bitInfo, int reserved1) throws EvioException {
- if (blockSizeMax < 100 || blockSizeMax > 1000000) { // TODO: restore limits
- throw new EvioException("Pick a block size >= 1K and <= 1M");
+ this(buf, blockSizeMax, blockCountMax, xmlDictionary, bitInfo, reserved1, false);
+ }
+
+ /**
+ * Create an <code>EventWriter</code> for writing events to a ByteBuffer.
+ *
+ * @param buf the buffer to write to.
+ * @param blockSizeMax the max blocksize to use which must be >= {@link #MIN_BLOCK_SIZE}
+ * and <= {@link #MAX_BLOCK_SIZE} ints.
+ * The size of the block will not be larger than this size
+ * unless a single event itself is larger.
+ * @param blockCountMax the max number of events in a single block which must be
+ * >= {@link #MIN_BLOCK_COUNT} and <= {@link #MAX_BLOCK_COUNT}.
+ * @param xmlDictionary dictionary in xml format or null if none.
+ * @param bitInfo set of bits to include in first block header.
+ * @param reserved1 set the value of the first "reserved" int in first block header.
+ * NOTE: only CODA (i.e. EMU) software should use this.
+ * @param append if <code>true</code>, all events to be written will be
+ * appended to the end of the buffer.
+ *
+ * @throws EvioException if blockSizeMax or blockCountMax exceed limits;
+ * if buf arg is null;
+ * if defined dictionary while appending;
+ */
+ private EventWriter(ByteBuffer buf, int blockSizeMax, int blockCountMax,
+ String xmlDictionary, BitSet bitInfo, int reserved1,
+ boolean append) throws EvioException {
+
+ initializeBuffer(buf, blockSizeMax, blockCountMax,
+ xmlDictionary, bitInfo, reserved1, append);
+ }
+
+
+ /**
+ * Encapsulate constructor initialization for buffers.
+ *
+ * @param buf the buffer to write to.
+ * @param blockSizeMax the max blocksize to use which must be >= {@link #MIN_BLOCK_SIZE}
+ * and <= {@link #MAX_BLOCK_SIZE} ints.
+ * The size of the block will not be larger than this size
+ * unless a single event itself is larger.
+ * @param blockCountMax the max number of events in a single block which must be
+ * >= {@link #MIN_BLOCK_COUNT} and <= {@link #MAX_BLOCK_COUNT}.
+ * @param xmlDictionary dictionary in xml format or null if none.
+ * @param bitInfo set of bits to include in first block header.
+ * @param reserved1 set the value of the first "reserved" int in first block header.
+ * NOTE: only CODA (i.e. EMU) software should use this.
+ * @param append if <code>true</code>, all events to be written will be
+ * appended to the end of the buffer.
+ *
+ * @throws EvioException if blockSizeMax or blockCountMax exceed limits;
+ * if buf arg is null;
+ * if defined dictionary while appending;
+ */
+ private void initializeBuffer(ByteBuffer buf, int blockSizeMax, int blockCountMax,
+ String xmlDictionary, BitSet bitInfo, int reserved1,
+ boolean append) throws EvioException {
+
+ if (blockSizeMax < MIN_BLOCK_SIZE) {
+ throw new EvioException("Max block size arg (" + blockSizeMax + ") must be >= " +
+ MIN_BLOCK_SIZE);
}
- if (blockCountMax < 1 || blockCountMax > 1000) {
- throw new EvioException("Pick a block count >= 1 and <= 1000");
+ if (blockSizeMax > MAX_BLOCK_SIZE) {
+ throw new EvioException("Max block size arg (" + blockSizeMax + ") must be <= " +
+ MAX_BLOCK_SIZE);
+ }
+
+ if (blockCountMax < MIN_BLOCK_COUNT) {
+ throw new EvioException("Max block count arg (" + blockCountMax + ") must be >= " +
+ MIN_BLOCK_COUNT);
+ }
+
+ if (blockCountMax > MAX_BLOCK_COUNT) {
+ throw new EvioException("Max block count arg (" + blockCountMax + ") must be <= " +
+ MAX_BLOCK_COUNT);
}
if (buf == null) {
throw new EvioException("Buffer arg cannot be null");
}
- toFile = false;
- this.buffer = buf;
- this.reserved1 = reserved1;
- this.blockSizeMax = blockSizeMax;
+ this.append = append;
+ this.buffer = buf;
+ this.byteOrder = buf.order();
+ this.reserved1 = reserved1;
+ this.blockSizeMax = blockSizeMax;
this.blockCountMax = blockCountMax;
this.xmlDictionary = xmlDictionary;
+ // Init variables
+ toFile = false;
+ closed = false;
+ blockNumber = 1;
+ eventsWritten = 0;
+ holdingListTotalBytes = 0;
+ eventHoldingList.clear();
+ bufferHeaderPosition = initialPosition = buf.position();
+
if (bitInfo != null) {
this.bitInfo = (BitSet)bitInfo.clone();
}
@@ -435,10 +764,59 @@
}
if (xmlDictionary != null) {
+ // Appending means not adding new dictionary
+ if (append) {
+ throw new EvioException("Cannot specify dictionary when appending");
+ }
this.bitInfo.set(0,true);
+ writeDictionary = true;
+ }
+
+ // For convenience, create buffer to hold a last, empty block header.
+ emptyLastHeader = ByteBuffer.allocate(32);
+ emptyLastHeader.order(byteOrder);
+ emptyLastHeader.putInt(8); // block len, words
+ emptyLastHeader.putInt(1); // block number
+ emptyLastHeader.putInt(8); // header len, words
+ emptyLastHeader.putInt(0); // event count
+ emptyLastHeader.putInt(0); // reserved 1
+ emptyLastHeader.putInt(0x204); // last block = true, version = 4
+ emptyLastHeader.putInt(0); // reserved 2
+ emptyLastHeader.putInt(0xc0da0100); // magic #
+ emptyLastHeader.flip();
+
+
+ try {
+ if (append) {
+ // Check endianness & version
+ examineFirstBufferHeader();
+
+ // Oops, gotta redo this since buffer
+ // has different byte order than specified.
+ if (byteOrder != buf.order()) {
+ emptyLastHeader.clear();
+ emptyLastHeader.order(byteOrder);
+ emptyLastHeader.putInt(8); // block len, words
+ emptyLastHeader.putInt(1); // block number
+ emptyLastHeader.putInt(8); // header len, words
+ emptyLastHeader.putInt(0); // event count
+ emptyLastHeader.putInt(0); // reserved 1
+ emptyLastHeader.putInt(0x204); // last block = true, version = 4
+ emptyLastHeader.putInt(0); // reserved 2
+ emptyLastHeader.putInt(0xc0da0100); // magic #
+ emptyLastHeader.flip();
+ }
+
+ // Prepare for appending by moving buffer position
+ toAppendPosition();
+ }
+ }
[truncated at 1000 lines; 994 more skipped]
jevio-base/src/main/java/org/jlab/coda/jevio
diff -u -r1.3 -r1.4
--- IEvioStructure.java 28 Feb 2012 19:41:36 -0000 1.3
+++ IEvioStructure.java 3 Apr 2013 20:07:46 -0000 1.4
@@ -59,14 +59,6 @@
*/
public String[] getStringData();
-// /**
-// * Gets the raw data as a String, if the type as indicated by the
-// * header is appropriate.
-// * @return the data as a String, or <code>null</code> if this makes no sense for the given type.
-// * (The only DataType it makes sense for is CHARSTAR8.)
-// */
-// public String getStringData();
-
/**
* Gets the raw data as a long array, if the type as indicated by the
* header is appropriate.
@@ -96,14 +88,14 @@
public short[] getShortData();
/**
- * Gets the raw data as a CompositeData object, if the type as indicated
+ * Gets the raw data as an array of CompositeData objects, if the type as indicated
* by the header is appropriate.<p>
*
- * @return the data as a CompositeData object,
+ * @return the data as an array of CompositeData objects,
* or <code>null</code> if this makes no sense for the given type.
* @throws EvioException if the data is internally inconsistent
*/
- public CompositeData getCompositeData() throws EvioException;
+ public CompositeData[] getCompositeData() throws EvioException;
/**
* Get the description from the name provider (dictionary), if there is one.
jevio-base/src/main/java/org/jlab/coda/jevio
diff -u -r1.3 -r1.4
--- EvioEvent.java 28 Feb 2012 19:41:36 -0000 1.3
+++ EvioEvent.java 3 Apr 2013 20:07:46 -0000 1.4
@@ -1,8 +1,5 @@
package org.jlab.coda.jevio;
-import java.util.List;
-import java.util.Vector;
-
import javax.swing.tree.DefaultTreeModel;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
@@ -25,6 +22,7 @@
/**
* As the event is parsed, it is parsed into a tree.
+ * Note that this is only used when parsing.
*/
protected DefaultTreeModel treeModel;
@@ -38,6 +36,7 @@
* There may be a dictionary in xml associated with this event. Or there may not.
*/
private String dictionaryXML;
+
/**
@@ -67,6 +66,46 @@
this(new BankHeader(tag, dataType, num));
}
+ /**
+ * Deep clone of BaseStructure does most of the work.
+ * Leave EvioBank's attachment as a reference. Only
+ * thing we need to do get rid of any cloned treeModel.
+ */
+ public Object clone() {
+ EvioEvent ev = (EvioEvent) super.clone();
+
+ // A treeModel is only created & used if this event was parsed into existence.
+ // To avoid keeping this unused and transient object, just get rid of it.
+ treeModel = null;
+
+ return ev;
+ }
+
+// /**
+// * Method to used to help reproduce an EvioEvent's treeModel object.
+// *
+// * @param model Model to add items to
+// * @param parent parent TreeNode object
+// * @param child child TreeNode object
+// * @param index index at which to place child in parent
+// */
+// static private void addChildToTree(DefaultTreeModel model, BaseStructure parent,
+// BaseStructure child, int index) {
+// // See if the child has children
+// Vector<BaseStructure> kids = child.getChildren();
+//
+// // If not, we're a leaf so add to tree model
+// if (kids == null || kids.size() < 1) {
+// model.insertNodeInto(parent, child, index); // (parent, child, index)
+// return;
+// }
+//
+// // If we have kids, add each to model recursively
+// int i=0;
+// for (BaseStructure kid : kids) {
+// addChildToTree(model, child, kid, i++);
+// }
+// }
/**
* Is there an XML dictionary associated with this event?
@@ -115,15 +154,15 @@
return s;
}
- /**
- * Get the tree model representing this event. This would have been generated
- * as the event was being parsed.
- * @return the tree model representing this event.
- */
- public DefaultTreeModel getTreeModel() {
- return treeModel;
- }
-
+ /**
+ * Get the tree model representing this event. This would have been generated
+ * as the event was being parsed.
+ * @return the tree model representing this event.
+ */
+ public DefaultTreeModel getTreeModel() {
+ return treeModel;
+ }
+
/**
* Inserts a child structure into the event's JTree. This is called when the event is being parsed,
* and when an event is being created.
@@ -134,83 +173,6 @@
treeModel.insertNodeInto(child, parent, parent.getChildCount());
}
- /**
- * Visit all the structures in this event (including the event itself--which is considered its own descendant).
- * This is similar to listening to the event as it is being parsed, but is done to a complete (already) parsed event.
- * @param listener an listener to notify as each structure is visited.
- */
- public void vistAllStructures(IEvioListener listener) {
- visitAllDescendants(this, listener, null);
- }
-
- /**
- * Visit all the structures in this event (including the event itself--which is considered its own descendant) in a depth first manner.
- * @param listener an listener to notify as each structure is visited.
- * @param filter an optional filter that must "accept" structures before they are passed to the listener. If <code>null</code>, all
- * structures are passed. In this way, specific types of structures can be captured.
- */
- public void vistAllStructures(IEvioListener listener, IEvioFilter filter) {
- visitAllDescendants(this, listener, filter);
- }
-
- /**
- * Visit all the descendants of a given structure (which is considered a descendant of itself.)
- *
- * @param structure the starting structure.
- * @param listener an listener to notify as each structure is visited.
- * @param filter an optional filter that must "accept" structures before they are passed to the listener. If
- * <code>null</code>, all structures are passed. In this way, specific types of structures can be
- * captured.
- */
- private void visitAllDescendants(BaseStructure structure, IEvioListener listener, IEvioFilter filter) {
-
- if (listener != null) {
- boolean accept = true;
- if (filter != null) {
- accept = filter.accept(structure.getStructureType(), structure);
- }
-
- if (accept) {
- listener.gotStructure(this, structure);
- }
- }
-
- if (!(structure.isLeaf())) {
- for (BaseStructure child : structure.getChildren()) {
- visitAllDescendants(child, listener, filter);
- }
- }
- }
-
- /**
- * Visit all the descendant structures, and collect those that pass a filter.
- * @param filter the filter that must be passed. If <code>null</code>, this will return all the structures.
- * @return a collection of all structures that are accepted by a filter.
- */
- public List<BaseStructure> getMatchingStructures(IEvioFilter filter) {
- final Vector<BaseStructure> structures = new Vector<BaseStructure>(25, 10);
-
- IEvioListener listener = new IEvioListener() {
- @Override
- public void startEventParse(EvioEvent evioEvent) { }
-
- @Override
- public void endEventParse(EvioEvent evioEvent) { }
-
- @Override
- public void gotStructure(EvioEvent evioEvent, IEvioStructure structure) {
- structures.add((BaseStructure)structure);
- }
- };
-
- vistAllStructures(listener, filter);
-
- if (structures.size() == 0) {
- return null;
- }
- return structures;
- }
-
/**
* This returns a number [1..] indicating which event this was in the event file from which
* it was read. It is not the "num" field from the header.
jevio-base/src/main/java/org/jlab/coda/jevio
diff -u -r1.4 -r1.5
--- ByteDataTransformer.java 20 Mar 2012 23:21:49 -0000 1.4
+++ ByteDataTransformer.java 3 Apr 2013 20:07:46 -0000 1.5
@@ -4,8 +4,6 @@
import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.*;
-import java.util.ArrayList;
-import java.util.List;
/**
* This utility class contains methods for transforming a raw byte array into arrays of
@@ -256,10 +254,15 @@
* @param data short array to convert
* @param byteOrder byte order of returned bytes (big endian if null)
* @return byte array representing short array or null if data is null
+ * @throws EvioException if data array has too many elements to convert to a byte array
*/
- public static byte[] toBytes(short[] data, ByteOrder byteOrder) {
+ public static byte[] toBytes(short[] data, ByteOrder byteOrder) throws EvioException {
if (data == null) return null;
+ if (data.length > Integer.MAX_VALUE/2) {
+ throw new EvioException("short array has too many elements to convert to byte array");
+ }
+
if (byteOrder == null) {
byteOrder = ByteOrder.BIG_ENDIAN;
}
@@ -282,15 +285,20 @@
* @param byteOrder byte order of written bytes (big endian if null)
* @param dest array in which to store written bytes
* @param off offset into dest array where returned bytes are placed
- * @throws EvioException if data is null, dest is null or too small, or offset negative
+ * @throws EvioException if data is null, dest is null or too small, or offset negative;
+ * if data array has too many elements to convert to a byte array
*/
public static void toBytes(short[] data, ByteOrder byteOrder, byte[] dest, int off)
- throws EvioException{
+ throws EvioException {
if (data == null || dest == null || dest.length < 2*data.length+off || off < 0) {
throw new EvioException("bad arg(s)");
}
+ if (data.length > Integer.MAX_VALUE/2) {
+ throw new EvioException("short array has too many elements to convert to byte array");
+ }
+
if (byteOrder == null) {
byteOrder = ByteOrder.BIG_ENDIAN;
}
@@ -371,10 +379,16 @@
* @param data int array to convert
* @param byteOrder byte order of returned bytes (big endian if null)
* @return byte array representing int array or null if data is null
+ * @throws EvioException if data array as too many elements to
+ * convert to a byte array
*/
- public static byte[] toBytes(int[] data, ByteOrder byteOrder) {
+ public static byte[] toBytes(int[] data, ByteOrder byteOrder) throws EvioException {
if (data == null) return null;
+ if (data.length > Integer.MAX_VALUE/4) {
+ throw new EvioException("int array has too many elements to convert to byte array");
+ }
+
if (byteOrder == null) {
byteOrder = ByteOrder.BIG_ENDIAN;
}
@@ -397,11 +411,18 @@
* @param length number of integers to conver to bytes
* @param byteOrder byte order of returned bytes (big endian if null)
* @return byte array representing int array or null if data is null
+ * @throws EvioException if data array has too many elements to
+ * convert to a byte array
*/
- public static byte[] toBytes(int[] data, int offset, int length, ByteOrder byteOrder) {
+ public static byte[] toBytes(int[] data, int offset, int length, ByteOrder byteOrder)
+ throws EvioException {
if (data == null) return null;
if (offset < 0 || length < 1) return null;
+ if (data.length > Integer.MAX_VALUE/4) {
+ throw new EvioException("int array has too many elements to convert to byte array");
+ }
+
if (byteOrder == null) {
byteOrder = ByteOrder.BIG_ENDIAN;
}
@@ -424,7 +445,8 @@
* @param byteOrder byte order of written bytes (big endian if null)
* @param dest array in which to store written bytes
* @param off offset into dest array where returned bytes are placed
- * @throws EvioException if data is null, dest is null or too small, or offset negative
+ * @throws EvioException if data is null, dest is null or too small, or offset negative;
+ * if data array has too many elements to convert to a byte array
*/
public static void toBytes(int[] data, ByteOrder byteOrder, byte[] dest, int off)
throws EvioException{
@@ -433,6 +455,10 @@
throw new EvioException("bad arg(s)");
}
+ if (data.length > Integer.MAX_VALUE/4) {
+ throw new EvioException("int array has too many elements to convert to byte array");
+ }
+
if (byteOrder == null) {
byteOrder = ByteOrder.BIG_ENDIAN;
}
@@ -454,10 +480,15 @@
* @param data int array to convert
* @param byteOrder byte order of returned bytes (big endian if null)
* @return byte array representing int array
+ * @throws EvioException if data array has too many elements to
+ * convert to a byte array
*/
- public static byte[] toBytesStream(int[] data, ByteOrder byteOrder) {
+ public static byte[] toBytesStream(int[] data, ByteOrder byteOrder) throws EvioException {
if (data == null) return null;
+ if (data.length > Integer.MAX_VALUE/4) {
+ throw new EvioException("int array has too many elements to convert to byte array");
+ }
ByteArrayOutputStream baos = new ByteArrayOutputStream(4*data.length);
DataOutputStream out = new DataOutputStream(baos);
@@ -564,10 +595,16 @@
* @param data long array to convert
* @param byteOrder byte order of returned bytes (big endian if null)
* @return byte array representing long array or null if data is null
+ * @throws EvioException if data array has too many elements to
+ * convert to a byte array
*/
- public static byte[] toBytes(long[] data, ByteOrder byteOrder) {
+ public static byte[] toBytes(long[] data, ByteOrder byteOrder) throws EvioException {
if (data == null) return null;
+ if (data.length > Integer.MAX_VALUE/8) {
+ throw new EvioException("long array has too many elements to convert to byte array");
+ }
+
if (byteOrder == null) {
byteOrder = ByteOrder.BIG_ENDIAN;
}
@@ -590,7 +627,8 @@
* @param byteOrder byte order of written bytes (big endian if null)
* @param dest array in which to store written bytes
* @param off offset into dest array where returned bytes are placed
- * @throws EvioException if data is null, dest is null or too small, or offset negative
+ * @throws EvioException if data is null, dest is null or too small, or offset negative;
+ * if data array has too many elements to convert to a byte array
*/
public static void toBytes(long[] data, ByteOrder byteOrder, byte[] dest, int off)
throws EvioException{
@@ -599,6 +637,10 @@
throw new EvioException("bad arg(s)");
}
+ if (data.length > Integer.MAX_VALUE/8) {
+ throw new EvioException("long array has too many elements to convert to byte array");
+ }
+
if (byteOrder == null) {
byteOrder = ByteOrder.BIG_ENDIAN;
}
@@ -647,10 +689,16 @@
* @param data float array to convert
* @param byteOrder byte order of returned bytes (big endian if null)
* @return byte array representing float array or null if data is null
+ * @throws EvioException if data array has too many elements to
+ * convert to a byte array
*/
- public static byte[] toBytes(float[] data, ByteOrder byteOrder) {
+ public static byte[] toBytes(float[] data, ByteOrder byteOrder) throws EvioException {
if (data == null) return null;
+ if (data.length > Integer.MAX_VALUE/4) {
+ throw new EvioException("float array has too many elements to convert to byte array");
+ }
+
if (byteOrder == null) {
byteOrder = ByteOrder.BIG_ENDIAN;
}
@@ -672,7 +720,8 @@
* @param byteOrder byte order of written bytes (big endian if null)
* @param dest array in which to store written bytes
* @param off offset into dest array where returned bytes are placed
- * @throws EvioException if data is null, dest is null or too small, or offset negative
+ * @throws EvioException if data is null, dest is null or too small, or offset negative;
+ * if data array has too many elements to convert to a byte array
*/
public static void toBytes(float[] data, ByteOrder byteOrder, byte[] dest, int off)
throws EvioException{
@@ -681,6 +730,10 @@
throw new EvioException("bad arg(s)");
}
+ if (data.length > Integer.MAX_VALUE/4) {
+ throw new EvioException("float array has too many elements to convert to byte array");
+ }
+
if (byteOrder == null) {
byteOrder = ByteOrder.BIG_ENDIAN;
}
@@ -728,10 +781,16 @@
* @param data double array to convert
* @param byteOrder byte order of returned bytes (big endian if null)
* @return byte array representing double array or null if data is null
+ * @throws EvioException if data array has too many elements to
+ * convert to a byte array
*/
- public static byte[] toBytes(double[] data, ByteOrder byteOrder) {
+ public static byte[] toBytes(double[] data, ByteOrder byteOrder) throws EvioException {
if (data == null) return null;
+ if (data.length > Integer.MAX_VALUE/8) {
+ throw new EvioException("double array has too many elements to convert to byte array");
+ }
+
if (byteOrder == null) {
byteOrder = ByteOrder.BIG_ENDIAN;
}
@@ -753,7 +812,8 @@
* @param byteOrder byte order of written bytes (big endian if null)
* @param dest array in which to store written bytes
* @param off offset into dest array where returned bytes are placed
- * @throws EvioException if data is null, dest is null or too small, or offset negative
+ * @throws EvioException if data is null, dest is null or too small, or offset negative;
+ * if data array has too many elements to convert to a byte array
*/
public static void toBytes(double[] data, ByteOrder byteOrder, byte[] dest, int off)
throws EvioException{
@@ -762,6 +822,10 @@
throw new EvioException("bad arg(s)");
}
+ if (data.length > Integer.MAX_VALUE/8) {
+ throw new EvioException("double array has too many elements to convert to byte array");
+ }
+
if (byteOrder == null) {
byteOrder = ByteOrder.BIG_ENDIAN;
}
@@ -784,10 +848,15 @@
* @param data double array to convert
* @param byteOrder byte order of returned bytes (big endian if null)
* @return byte array representing double array or null if data is null
+ * @throws EvioException if data array has too many elements to
+ * convert to a byte array
*/
- public static byte[] toBytes2(double[] data, ByteOrder byteOrder) {
+ public static byte[] toBytes2(double[] data, ByteOrder byteOrder) throws EvioException {
if (data == null) return null;
+ if (data.length > Integer.MAX_VALUE/8) {
+ throw new EvioException("double array has too many elements to convert to byte array");
+ }
byte[] stor = new byte[8];
byte[] byts = new byte[data.length*8];
@@ -815,7 +884,8 @@
* @param byteOrder byte order of returned bytes (big endian if null)
* @param dest array in which to store returned bytes
* @param off offset into dest array where returned bytes are placed
- * @throws EvioException if data is null, dest is null or too small, or offset negative
+ * @throws EvioException if data is null, dest is null or too small, or offset negative;
+ * if data array has too many elements to convert to a byte array
*/
public static void toBytes2(double[] data, ByteOrder byteOrder, byte[] dest, int off)
throws EvioException{
@@ -823,6 +893,9 @@
if (data == null || dest == null || dest.length < 8*data.length+off || off < 0) {
throw new EvioException("bad arg(s)");
}
+ if (data.length > Integer.MAX_VALUE/8) {
+ throw new EvioException("double array has too many elements to convert to byte array");
+ }
byte[] stor = new byte[8];
jevio-base/src/main/java/org/jlab/coda/jevio
diff -u -r1.1 -r1.2
--- IBlockHeader.java 28 Feb 2012 19:41:36 -0000 1.1
+++ IBlockHeader.java 3 Apr 2013 20:07:46 -0000 1.2
@@ -96,11 +96,12 @@
/**
* Gives the bytes remaining in this block (physical record) given a buffer position. The position is an absolute
* position in a byte buffer. This assumes that the absolute position in <code>bufferStartingPosition</code> is
- * being maintained properly by the reader.
+ * being maintained properly by the reader. No block is longer than 2.1GB - 31 bits of length. This is for
+ * practical reasons - so a block can be read into a single byte array.
*
* @param position the absolute current position is a byte buffer.
* @return the number of bytes remaining in this block (physical record.)
- * @throws org.jlab.coda.jevio.EvioException
+ * @throws EvioException
*/
int bytesRemaining(int position) throws EvioException;
jevio-base/src/main/java/org/jlab/coda/jevio
diff -u -r1.4 -r1.5
--- DataType.java 20 Mar 2012 23:21:49 -0000 1.4
+++ DataType.java 3 Apr 2013 20:07:46 -0000 1.5
@@ -1,6 +1,5 @@
package org.jlab.coda.jevio;
-import org.omg.CORBA.NamedValue;
/**
* This an enum used to convert data type numerical values to a more meaningful name. For example, the data type with
jevio-base/src/main/java/org/jlab/coda/jevio
diff -u -r1.3 -r1.4
--- IEvioListener.java 28 Feb 2012 19:41:36 -0000 1.3
+++ IEvioListener.java 3 Apr 2013 20:07:46 -0000 1.4
@@ -13,23 +13,23 @@
/**
* Called when a structure is read while parsing an event.
*
- * NOTE: the user should NOT modify the event or the structure.
+ * NOTE: the user should NOT modify the arguments.
*
- * @param evioEvent the data type, BANK, SEGMENT, or TAGSEGMENT
+ * @param topStructure the evio structure at the top of the search/parse
* @param structure the full structure, including header
*/
- public void gotStructure(EvioEvent evioEvent, IEvioStructure structure);
+ public void gotStructure(BaseStructure topStructure, IEvioStructure structure);
/**
- * Starting to parse a new event.
- * @param evioEvent the event in question.
+ * Starting to parse a new evio structure.
+ * @param structure the evio structure in question.
*/
- public void startEventParse(EvioEvent evioEvent);
+ public void startEventParse(BaseStructure structure);
/**
- * Done parsing a new event.
- * @param evioEvent the event in question.
+ * Done parsing a new evio structure.
+ * @param structure the evio structure in question.
*/
- public void endEventParse(EvioEvent evioEvent);
+ public void endEventParse(BaseStructure structure);
}
jevio-base/src/main/java/org/jlab/coda/jevio
diff -u -r1.2 -r1.3
--- EvioReader.java 20 Mar 2012 23:21:49 -0000 1.2
+++ EvioReader.java 3 Apr 2013 20:07:46 -0000 1.3
@@ -3,6 +3,7 @@
import java.io.*;
import java.nio.*;
import java.nio.channels.FileChannel;
+import java.util.ArrayList;
import javax.xml.stream.FactoryConfigurationError;
import javax.xml.stream.XMLOutputFactory;
@@ -10,13 +11,13 @@
import javax.xml.stream.XMLStreamWriter;
/**
- * This is the a class of interest to the user. It is used to read an evio version 4 or earlier
+ * This is a class of interest to the user. It is used to read an evio version 4 or earlier
* format file or buffer. Create an <code>EvioReader</code> from a <code>File</code>
* object corresponding to an event file, and from this class you can test the file
- * for consistency and, more importantly, you can call <code>parseNextEvent</code>
- * to get new events and to stream the embedded structures to an IEvioListener.
- * The same can be done by creating an <code>EvioReader</code> from a
- * <code>ByteBuffer</code>.
+ * for consistency and, more importantly, you can call {@link #parseNextEvent} or
+ * {@link #parseEvent(int)} to get new events and to stream the embedded structures
+ * to an IEvioListener. The same can be done by creating an <code>EvioReader</code>
+ * from a <code>ByteBuffer</code>.
*
* The streaming effect of parsing an event is that the parser will read the event and hand off structures,
* such as banks, to any IEvioListeners. For those familiar with XML, the event is processed SAX-like.
@@ -25,9 +26,6 @@
* As an alternative to stream processing, after an event is parsed, the user can use the events treeModel
* for access to the structures. For those familiar with XML, the event is processed DOM-like.
* <p>
- * NOTE: Even though this class has a constructor that accepts an i/o mode, that is for backwards
- * compatibility only. An <code>EvioReader</code> is used for reading and parsing events only.
- * To write an event file or buffer, use an <code>EventWriter</code> object.
*
* @author heddle
* @author timmer
@@ -72,6 +70,10 @@
private static final String ROOT_ELEMENT = "evio-data";
+ /** Skip through a file/buffer and store positions of all the events
+ * in random access mode for versions 4+. */
+ private final ArrayList<Integer> eventPositions = new ArrayList<Integer>(10000);
+
/** Used to assign a transient number [1..n] to events as they are being read. */
private int eventNumber = 0;
@@ -104,9 +106,6 @@
/** Block number expected when reading. Used to check sequence of blocks. */
private int blockNumberExpected = 1;
- /** Difference between expected & actual block numbers when reading. For warning messages. */
- private int blockNumberDiff;
-
/** If true, throw an exception if block numbers are out of sequence. */
private boolean checkBlockNumberSequence;
@@ -142,8 +141,65 @@
private MappedByteBuffer mappedByteBuffer;
+ //------------------------
+ // EvioReader's state
+ //------------------------
+
+ /**
+ * This class stores the state of this reader so it can be recovered
+ * after a state-changing method has been called -- like {@link #rewind()}.
+ */
+ private class ReaderState {
+ private boolean lastBlock;
+ private int eventNumber;
+ private int byteBufferPosition;
+ private int blockNumberExpected;
+ private BlockHeaderV2 blockHeader2;
+ private BlockHeaderV4 blockHeader4;
+ }
+
+ /**
+ * This method saves the current state of this EvioReader object.
+ * @return the current state of this EvioReader object.
+ */
+ private ReaderState getState() {
+ ReaderState currentState = new ReaderState();
+ currentState.lastBlock = lastBlock;
+ currentState.eventNumber = eventNumber;
+ currentState.byteBufferPosition = byteBuffer.position();
+ currentState.blockNumberExpected = blockNumberExpected;
+ if (evioVersion > 3) {
+ currentState.blockHeader4 = (BlockHeaderV4)blockHeader4.clone();
+ }
+ else {
+ currentState.blockHeader2 = (BlockHeaderV2)blockHeader2.clone();
+ }
+
+ return currentState;
+ }
+
+
+ /**
+ * This method restores a previously saved state of this EvioReader object.
+ * @param state a previously stored state of this EvioReader object.
+ */
+ private void restoreState(ReaderState state) {
+ lastBlock = state.lastBlock;
+ eventNumber = state.eventNumber;
+ byteBuffer.position(state.byteBufferPosition);
+
+ blockNumberExpected = state.blockNumberExpected;
+ if (evioVersion > 3) {
+ blockHeader = blockHeader4 = state.blockHeader4;
+ }
+ else {
+ blockHeader = blockHeader2 = state.blockHeader2;
+ }
+ }
+ //------------------------
+
private void printBuffer(ByteBuffer buf, int lenInInts) {
IntBuffer ibuf = buf.asIntBuffer();
for (int i=0; i < lenInInts; i++) {
@@ -157,70 +213,174 @@
* @param path the full path to the file that contains events.
* For writing event files, use an <code>EventWriter</code> object.
* @see EventWriter
- * @throws IOException if read failure
+ * @throws IOException if read failure
+ * @throws EvioException if file arg is null
*/
- public EvioReader(String path) throws IOException {
+ public EvioReader(String path) throws EvioException, IOException {
this(new File(path));
}
/**
* Constructor for reading an event file.
*
+ * @param path the full path to the file that contains events.
+ * For writing event files, use an <code>EventWriter</code> object.
+ * @param checkBlkNumSeq if <code>true</code> check the block number sequence
+ * and throw an exception if it is not sequential starting
+ * with 1
+ * @see EventWriter
+ * @throws IOException if read failure
+ * @throws EvioException if file arg is null;
+ * if first block number != 1 when checkBlkNumSeq arg is true
+ */
+ public EvioReader(String path, boolean checkBlkNumSeq) throws EvioException, IOException {
+ this(new File(path), checkBlkNumSeq);
+ }
+
+ /**
+ * Constructor for reading an event file.
+ *
* @param file the file that contains events.
- * @throws IOException if read failure
+ * @see EventWriter
+ * @throws IOException if read failure
+ * @throws EvioException if file arg is null
+ */
+ public EvioReader(File file) throws EvioException, IOException {
+ this(file, false);
+ }
+
+
+ /**
+ * Constructor for reading an event file.
+ *
+ * @param file the file that contains events.
+ * @param checkBlkNumSeq if <code>true</code> check the block number sequence
+ * and throw an exception if it is not sequential starting
+ * with 1
+ * @see EventWriter
+ * @throws IOException if read failure
+ * @throws EvioException if file arg is null;
+ * if first block number != 1 when checkBlkNumSeq arg is true
*/
- public EvioReader(File file) throws IOException {
+ public EvioReader(File file, boolean checkBlkNumSeq)
+ throws EvioException, IOException {
+ if (file == null) {
+ throw new EvioException("File arg is null");
+ }
+
+ checkBlockNumberSequence = checkBlkNumSeq;
initialPosition = 0;
FileInputStream fileInputStream = new FileInputStream(file);
path = file.getAbsolutePath();
- FileChannel inputChannel = fileInputStream.getChannel();
- mapFile(inputChannel);
- inputChannel.close(); // this object is no longer needed
- // Read first event in case it's a dictionary, we'll also
- // find the file's endianness & evio version #.
- try {
- nextEvent();
- }
- catch (EvioException e) {
- throw new IOException("Failed reading first event", e);
+ FileChannel fileChannel = fileInputStream.getChannel();
+ mapFile(fileChannel);
+ fileChannel.close(); // this object is no longer needed
+
+ // Read first block header and find the file's endianness & evio version #.
+ // If there's a dictionary, read that too.
+ if (getFirstHeader() != ReadStatus.SUCCESS) {
+ throw new IOException("Failed reading first block header/dictionary");
}
- // reset buffer to beginning
- rewind();
+ // For the lastest evio format, generate a table
+ // of all event positions in buffer for random access.
+ if (evioVersion > 3) {
+ eventCount = 0;
+ generateEventPositionTable();
+ }
parser = new EventParser();
}
+ /**
+ * Constructor for reading a buffer.
+ *
+ * @param byteBuffer the buffer that contains events.
+ * @see EventWriter
+ * @throws IOException if read failure
+ * @throws EvioException if file arg is null
+ */
+ public EvioReader(ByteBuffer byteBuffer) throws EvioException, IOException {
+ this(byteBuffer, false);
+ }
/**
* Constructor for reading a buffer.
- * TODO: Perhaps a method to reset the buffer would be more efficient than creating a new object.
*
* @param byteBuffer the buffer that contains events.
- * @throws IOException if read failure
+ * @param checkBlkNumSeq if <code>true</code> check the block number sequence
+ * and throw an exception if it is not sequential starting
+ * with 1
+ * @see EventWriter
+ * @throws IOException if read failure
+ * @throws EvioException if file arg is null;
+ * if first block number != 1 when checkBlkNumSeq arg is true
*/
- public EvioReader(ByteBuffer byteBuffer) throws IOException {
+ public EvioReader(ByteBuffer byteBuffer, boolean checkBlkNumSeq)
+ throws EvioException, IOException {
+
+ if (byteBuffer == null) {
+ throw new EvioException("Buffer arg is null");
+ }
+
+ checkBlockNumberSequence = checkBlkNumSeq;
initialPosition = byteBuffer.position();
this.byteBuffer = byteBuffer;
- // Read first event in case it's a dictionary, we'll also
- // find the file's endianness & evio version #.
- try {
- nextEvent();
- }
- catch (EvioException e) {
- throw new IOException("Failed reading first event", e);
+ // Read first block header and find the file's endianness & evio version #.
+ // If there's a dictionary, read that too.
+ if (getFirstHeader() != ReadStatus.SUCCESS) {
+ throw new IOException("Failed reading first block header/dictionary");
}
- // reset buffer to beginning
- rewind();
+ // For the lastest evio format, generate a table
+ // of all event positions in buffer for random access.
+ if (evioVersion > 3) {
+ eventCount = 0;
+ generateEventPositionTable();
+ }
parser = new EventParser();
}
/**
+ * This method can be used to avoid creating additional EvioReader
+ * objects by reusing this one with another buffer. The method
+ * {@link #close()} is called before anything else.
+ *
+ * @param buf ByteBuffer to be read
+ * @throws IOException if read failure
+ * @throws EvioException if first block number != 1 when checkBlkNumSeq arg is true
+ */
+ public synchronized void setBuffer(ByteBuffer buf) throws EvioException, IOException {
+
+ close();
+
+ lastBlock = false;
+ eventNumber = 0;
+ eventCount = -1;
+ blockNumberExpected = 1;
+ dictionaryXML = null;
+ initialPosition = buf.position();
+ this.byteBuffer = buf;
+
+ // Read first block header and find the file's endianness & evio version #.
+ // If there's a dictionary, read that too.
+ if (getFirstHeader() != ReadStatus.SUCCESS) {
+ throw new IOException("Failed reading first block header/dictionary");
+ }
+
+ // For the lastest evio format, generate a table
+ // of all event positions in buffer for random access.
+ if (evioVersion > 3) {
+ eventCount = 0;
+ generateEventPositionTable();
+ }
+ }
+
+ /**
* Is this reader checking the block number sequence and
* throwing an exception is it's not sequential and starting with 1?
* @return <code>true</code> if checking block number sequence, else <code>false</code>
@@ -230,25 +390,6 @@
}
/**
- * Set whether this reader will check the block number sequence
- * and throw an exception is it's not sequential and starting with 1.
- * This method must be called before parsing events if you want to
- * change from not checking to checking.
- * @param checkBlockNumberSequence <code>true</code> to check block number sequence,
- * else <code>false</code>
- */
- public void checkBlockNumberSequence(boolean checkBlockNumberSequence) {
- // Are we at the top of the file?
- // If not, do NOT start checking block
- // numbers part way into reading events.
- if (byteBuffer.position() != 0 && checkBlockNumberSequence) {
- return;
- }
-
- this.checkBlockNumberSequence = checkBlockNumberSequence;
- }
-
- /**
* Get the evio version number.
* @return evio version number.
*/
@@ -352,112 +493,180 @@
}
/**
- * Reads the block (physical record) header. Assumes the mapped buffer is positioned
- * at the start of the next block header (physical record.)<br>
+ * Generate a table (ArrayList) of positions of events in file/buffer.
+ * This method does <b>not</b> affect the byteBuffer position, eventNumber,
+ * or lastBlock values. Valid only in versions 4 and later.
*
- * A Bank header is 8, 32-bit ints. The first int is the size of the block in ints
- * (not counting the length itself, i.e., the number of ints to follow).
- *
- * Most users should have no need for this method, since most applications do not
- * care about the block (physical record) header.
- *
- * @return status of read attempt
+ * @throws EvioException if version 3 or earlier
*/
- protected synchronized ReadStatus nextBlockHeader() {
+ private void generateEventPositionTable() throws EvioException {
- // we already read the last block header
- if (lastBlock) {
- return ReadStatus.END_OF_FILE;
+ if (evioVersion < 4) {
+ throw new EvioException("Unsupported version (" + evioVersion + ")");
}
- // have enough remaining?
+ int i, position, byteLen, bytesLeft, blockEventCount, blockHdrSize;
+ boolean curLastBlock=false, firstBlock=true, hasDictionary=false;
+
+
+ // Start at the beginning of byteBuffer without changing
+ // its current position. Do this with absolute gets.
+ position = initialPosition;
+ bytesLeft = byteBuffer.limit() - position;
+// System.out.println("generatePositionTable: ");
+// System.out.println(" byte buf cap = " + byteBuffer.capacity());
+// System.out.println(" byte buf lim = " + byteBuffer.limit());
+// System.out.println(" byte buf pos = " + byteBuffer.position());
+// System.out.println(" bytesLeft = " + bytesLeft);
+// System.out.println(" (initial) pos = " + position);
+// System.out.println();
+
+ while (!curLastBlock) {
+//System.out.println("generatePositionTable: pos = " + position +
+// ", ver pos = " + (position + 4*BlockHeaderV4.EV_VERSION) +
+// ", h siz pos = " + (position + 4*BlockHeaderV4.EV_HEADERSIZE) +
+// ", ev count pos = " + (position + 4*BlockHeaderV4.EV_COUNT));
+
+ // Look at block header to get info. Swapping is taken care of.
+ i = byteBuffer.getInt(position + 4*BlockHeaderV4.EV_VERSION);
+ blockHdrSize = byteBuffer.getInt(position + 4*BlockHeaderV4.EV_HEADERSIZE);
+ blockEventCount = byteBuffer.getInt(position + 4*BlockHeaderV4.EV_COUNT);
+
+ eventCount += blockEventCount;
+ curLastBlock = BlockHeaderV4.isLastBlock(i);
+ if (firstBlock) hasDictionary = BlockHeaderV4.hasDictionary(i);
+
+// System.out.println(" ver = 0x" + Integer.toHexString(i));
+// System.out.println(" hdr size = 0x" + Integer.toHexString(blockHdrSize));
+// System.out.println(" blk count = 0x" + Integer.toHexString(blockEventCount));
+// System.out.println(" last blk = " + curLastBlock);
+// System.out.println(" has dict = " + hasDictionary);
+// System.out.println();
+
+ // Hop over block header to data
+ position += 4*blockHdrSize;
+ bytesLeft -= 4*blockHdrSize;
+
+//System.out.println(" hopped blk hdr, bytesLeft = " + bytesLeft + ", pos = " + position);
+ // Check for a dictionary - the first event in the first block.
+ // It's not included in the header block count, but we must take
+ // it into account by skipping over it.
+ if (firstBlock && hasDictionary) {
+ firstBlock = false;
+
+ // Get its length - bank's len does not include itself
+ byteLen = 4*(byteBuffer.getInt(position) + 1);
+
+ // Skip over dictionary
+ position += byteLen;
+ bytesLeft -= byteLen;
+//System.out.println(" hopped dict, bytesLeft = " + bytesLeft + ", pos = " + position);
+ }
+
+ // For each event in block, store its location
+ for (i=0; i < blockEventCount; i++) {
+ // Sanity check - must have at least 1 header's amount left
+ if (bytesLeft < 8) {
+ throw new EvioException("File/buffer bad format");
+ }
+
+ // Store current position
+ eventPositions.add(position);
+
+ // Get length of current event (including full header)
+ byteLen = 4*(byteBuffer.getInt(position) + 1);
+
+ position += byteLen;
+ bytesLeft -= byteLen;
+//System.out.println(" hopped event, bytesLeft = " + bytesLeft + ", pos = " + position + "\n");
+ }
+ }
+
+ }
+
+
+ /**
+ * Reads part of the first block (physical record) header in order to determine
+ * the evio version # and endianness of the file or buffer in question. These things
+ * do <b>not</b> need to be examined in subsequent block headers.
+ *
+ * @return status of read attempt
+ */
+ protected synchronized ReadStatus getFirstHeader() {
+
+ // Have enough remaining bytes to read?
if (byteBuffer.remaining() < 32) {
byteBuffer.clear();
return ReadStatus.END_OF_FILE;
}
try {
-// System.out.println("Print header =\n");
-// printBuffer(byteBuffer, 8);
- // Set the byte order to match the file's ordering.
+ // Set the byte order to match the buffer/file's ordering.
// Check the magic number for endianness. This requires
// peeking ahead 7 ints or 28 bytes. Set the endianness
// once we figure out what it is (buffer defaults to big endian).
byteOrder = byteBuffer.order();
int magicNumber = byteBuffer.getInt(MAGIC_OFFSET);
-//System.out.println("nextBlockHeader: actual magic number = " + Integer.toHexString(BlockHeaderV4.MAGIC_NUMBER));
-//System.out.println("nextBlockHeader: read magic number as " + Integer.toHexString(magicNumber));
if (magicNumber != IBlockHeader.MAGIC_NUMBER) {
- // Originally checked ByteOrder.nativeOrder() which is NOT what you want - timmer
if (byteOrder == ByteOrder.BIG_ENDIAN) {
byteOrder = ByteOrder.LITTLE_ENDIAN;
-//System.out.println("nextBlockHeader: reset byte order to " + ByteOrder.LITTLE_ENDIAN);
}
else {
byteOrder = ByteOrder.BIG_ENDIAN;
-//System.out.println("nextBlockHeader: reset byte order to " + ByteOrder.BIG_ENDIAN);
}
byteBuffer.order(byteOrder);
- // reread magic number to make sure things are OK
+ // Reread magic number to make sure things are OK
magicNumber = byteBuffer.getInt(MAGIC_OFFSET);
if (magicNumber != IBlockHeader.MAGIC_NUMBER) {
System.out.println("ERROR reread magic # (" + magicNumber + ") & still not right");
return ReadStatus.EVIO_EXCEPTION;
}
}
- else {
-//System.out.println("nextBlockHeader: byte order = " + byteOrder);
- }
-
// Check the version number. This requires peeking ahead 5 ints or 20 bytes.
- // Use the correct class for the block header once we know the version.
- int version = byteBuffer.getInt(VERSION_OFFSET) & VERSION_MASK;
-//System.out.println("nextBlockHeader: version = " + version);
-
- // TODO: wrap ByteBuffer with IntBuffer for faster reading
+ evioVersion = byteBuffer.getInt(VERSION_OFFSET) & VERSION_MASK;
+ if (evioVersion < 1) {
+ return ReadStatus.EVIO_EXCEPTION;
+ }
+//System.out.println("Evio version# = " + evioVersion);
try {
- if (version > 0 && version < 4) {
- // cache the starting position
- blockHeader2.setBufferStartingPosition(byteBuffer.position());
-
- // read the header data.
- blockHeader2.setSize(byteBuffer.getInt());
- blockHeader2.setNumber(byteBuffer.getInt());
- blockHeader2.setHeaderLength(byteBuffer.getInt());
- blockHeader2.setStart(byteBuffer.getInt());
- blockHeader2.setEnd(byteBuffer.getInt());
- // skip version
- byteBuffer.getInt();
- blockHeader2.setVersion(version);
- blockHeader2.setReserved1(byteBuffer.getInt());
- blockHeader2.setMagicNumber(byteBuffer.getInt());
- blockHeader = blockHeader2;
- }
- else if (version >= 4) {
- // cache the starting position
+ if (evioVersion >= 4) {
+ // Cache the starting position
blockHeader4.setBufferStartingPosition(byteBuffer.position());
-
- // read the header data.
+ // Read the header data.
blockHeader4.setSize(byteBuffer.getInt());
blockHeader4.setNumber(byteBuffer.getInt());
blockHeader4.setHeaderLength(byteBuffer.getInt());
blockHeader4.setEventCount(byteBuffer.getInt());
- // unused
- byteBuffer.getInt();
- // use 6th word to set bit info
+ blockHeader4.setReserved1(byteBuffer.getInt());
+
+ // Use 6th word to set bit info & version
blockHeader4.parseToBitInfo(byteBuffer.getInt());
- blockHeader4.setVersion(version);
+ blockHeader4.setVersion(evioVersion);
lastBlock = blockHeader4.getBitInfo(1);
- // unused
- byteBuffer.getInt();
+
+ blockHeader4.setReserved2(byteBuffer.getInt());
blockHeader4.setMagicNumber(byteBuffer.getInt());
blockHeader = blockHeader4;
+ // Deal with non-standard header lengths here
+ int headerLenDiff = blockHeader4.getHeaderLength() - BlockHeaderV4.HEADER_SIZE;
+ // If too small quit with error since headers have a minimum size
+ if (headerLenDiff < 0) {
+ return ReadStatus.EVIO_EXCEPTION;
+ }
+ // If bigger, read extra ints
+ else if (headerLenDiff > 0) {
+ for (int i=0; i < headerLenDiff; i++) {
+//System.out.println("Getting extra header int");
+ byteBuffer.getInt();
+ }
+ }
+
//System.out.println("BlockHeader v4:");
//System.out.println(" block length = " + blockHeader4.getSize() + " ints");
//System.out.println(" block number = " + blockHeader4.getNumber());
@@ -469,35 +678,38 @@
//System.out.println(" magic number = " + Integer.toHexString(blockHeader4.getMagicNumber()));
//System.out.println();
+ // Is there a dictionary? If so, read it here.
+ if (blockHeader4.hasDictionary()) {
+ readDictionary();
+ }
}
else {
- // error
-System.err.println("ERROR unsupported evio version number in file or buffer");
- return ReadStatus.EVIO_EXCEPTION;
- }
+ // cache the starting position
+ blockHeader2.setBufferStartingPosition(byteBuffer.position());
- // each file is restricted to one version, so set it once
- if (evioVersion < 1) {
- evioVersion = version;
+ // read the header data.
+ blockHeader2.setSize(byteBuffer.getInt());
+ blockHeader2.setNumber(byteBuffer.getInt());
+ blockHeader2.setHeaderLength(byteBuffer.getInt());
+ blockHeader2.setStart(byteBuffer.getInt());
+ blockHeader2.setEnd(byteBuffer.getInt());
+ // skip version
+ byteBuffer.getInt();
+ blockHeader2.setVersion(evioVersion);
+ blockHeader2.setReserved1(byteBuffer.getInt());
+ blockHeader2.setMagicNumber(byteBuffer.getInt());
+ blockHeader = blockHeader2;
}
// check block number if so configured
if (checkBlockNumberSequence) {
if (blockHeader.getNumber() != blockNumberExpected) {
-System.out.println("block number out of sequence, got " +
- blockHeader.getNumber() + " expecting " + blockNumberExpected);
- blockNumberExpected++;
+
+System.out.println("block # out of sequence, got " + blockHeader.getNumber() +
+ " expecting " + blockNumberExpected);
+
return ReadStatus.EVIO_EXCEPTION;
}
- }
- // otherwise print out a warning if number skips
- else {
- if (blockHeader.getNumber() != blockNumberExpected + blockNumberDiff) {
- System.out.println("block number out of sequence, got " +
- blockHeader.getNumber() + " expecting " +
- (blockNumberExpected + blockNumberDiff));
- }
- blockNumberDiff = blockHeader.getNumber() - blockNumberExpected;
blockNumberExpected++;
}
}
@@ -505,6 +717,7 @@
e.printStackTrace();
return ReadStatus.EVIO_EXCEPTION;
}
+
}
catch (BufferUnderflowException a) {
System.err.println("ERROR endOfBuffer " + a);
@@ -517,19 +730,318 @@
/**
- * Get the next event in the file. As useful as this sounds, most applications will probably call
- * {@link #parseNextEvent() parseNextEvent} instead, since it combines combines getting the next
- * event with parsing the next event.<p>
- * In evio version 4, events no longer cross block boundaries. There are only one or more
- * complete events in each block. No changes were made to this method from versions 1-3 in order
- * to read the version 4 format as it is subset of versions 1-3 with variable block length.
- *
- * @return the next event in the file. On error it throws an EvioException.
- * On end of file, it returns <code>null</code>.
- * @throws EvioException if failed read due to bad buffer format
- */
- public synchronized EvioEvent nextEvent() throws EvioException {
- EvioEvent event = new EvioEvent();
+ * Reads the block (physical record) header. Assumes the mapped buffer is positioned
+ * at the start of the next block header (physical record.) By the time this is called,
+ * the version # and byte order have already been determined. Not necessary to do that
+ * for each block header that's read.<br>
+ *
+ * A Bank header is 8, 32-bit ints. The first int is the size of the block in ints
+ * (not counting the length itself, i.e., the number of ints to follow).
+ *
+ * Most users should have no need for this method, since most applications do not
+ * care about the block (physical record) header.
+ *
+ * @return status of read attempt
+ */
+ protected synchronized ReadStatus nextBlockHeader() {
+
+ // We already read the last block header
+ if (lastBlock) {
+ return ReadStatus.END_OF_FILE;
+ }
+
+ // Have enough remaining?
+ if (byteBuffer.remaining() < 32) {
+ byteBuffer.clear();
+ return ReadStatus.END_OF_FILE;
+ }
+
+ try {
+ if (evioVersion >= 4) {
+ // Cache the starting position
+ blockHeader4.setBufferStartingPosition(byteBuffer.position());
+
+ // Read the header data.
+ blockHeader4.setSize(byteBuffer.getInt());
+ blockHeader4.setNumber(byteBuffer.getInt());
+ blockHeader4.setHeaderLength(byteBuffer.getInt());
+ blockHeader4.setEventCount(byteBuffer.getInt());
+ blockHeader4.setReserved1(byteBuffer.getInt());
+ // Use 6th word to set bit info
+ blockHeader4.parseToBitInfo(byteBuffer.getInt());
+ blockHeader4.setVersion(evioVersion);
+ lastBlock = blockHeader4.getBitInfo(1);
+ blockHeader4.setReserved2(byteBuffer.getInt());
+ blockHeader4.setMagicNumber(byteBuffer.getInt());
+ blockHeader = blockHeader4;
+
+ // Deal with non-standard header lengths here
+ int headerLenDiff = blockHeader4.getHeaderLength() - BlockHeaderV4.HEADER_SIZE;
+ // If too small quit with error since headers have a minimum size
+ if (headerLenDiff < 0) {
+ return ReadStatus.EVIO_EXCEPTION;
+ }
+ // If bigger, read extra ints
+ else if (headerLenDiff > 0) {
+ for (int i=0; i < headerLenDiff; i++) {
+ byteBuffer.getInt();
+ }
+ }
+
+//System.out.println("BlockHeader v4:");
+//System.out.println(" block length = " + blockHeader4.getSize() + " ints");
+//System.out.println(" block number = " + blockHeader4.getNumber());
+//System.out.println(" header length = " + blockHeader4.getHeaderLength() + " ints");
+//System.out.println(" event count = " + blockHeader4.getEventCount());
+//System.out.println(" version = " + blockHeader4.getVersion());
+//System.out.println(" has Dict = " + blockHeader4.getBitInfo(0));
+//System.out.println(" is End = " + lastBlock);
+//System.out.println(" magic number = " + Integer.toHexString(blockHeader4.getMagicNumber()));
+//System.out.println();
+
+ }
+ else if (evioVersion < 4) {
+ // cache the starting position
+ blockHeader2.setBufferStartingPosition(byteBuffer.position());
+
+ // read the header data.
+ blockHeader2.setSize(byteBuffer.getInt());
+ blockHeader2.setNumber(byteBuffer.getInt());
+ blockHeader2.setHeaderLength(byteBuffer.getInt());
+ blockHeader2.setStart(byteBuffer.getInt());
+ blockHeader2.setEnd(byteBuffer.getInt());
+ // skip version
+ byteBuffer.getInt();
+ blockHeader2.setVersion(evioVersion);
+ blockHeader2.setReserved1(byteBuffer.getInt());
+ blockHeader2.setMagicNumber(byteBuffer.getInt());
+ blockHeader = blockHeader2;
+ }
+ else {
+ // bad version # - should never happen
+ return ReadStatus.EVIO_EXCEPTION;
+ }
+
+ // check block number if so configured
+ if (checkBlockNumberSequence) {
+ if (blockHeader.getNumber() != blockNumberExpected) {
+
+System.out.println("block # out of sequence, got " + blockHeader.getNumber() +
+ " expecting " + blockNumberExpected);
+
+ return ReadStatus.EVIO_EXCEPTION;
+ }
+ blockNumberExpected++;
+ }
+ }
+ catch (EvioException e) {
+ e.printStackTrace();
+ return ReadStatus.EVIO_EXCEPTION;
+ }
+ catch (BufferUnderflowException a) {
+System.err.println("ERROR endOfBuffer " + a);
+ byteBuffer.clear();
+ return ReadStatus.UNKNOWN_ERROR;
+ }
+
+ return ReadStatus.SUCCESS;
+ }
+
+
+ /**
+ * This method is only called once at the very beginning if buffer is known to have
+ * a dictionary. It then reads that dictionary. Only called in versions 4 & up.
+ *
+ * @return <code>true</code> if dictionary was found & read, else <code>false</code>.
+ * @throws EvioException if failed read due to bad buffer format;
+ * if version 3 or earlier
+ */
+ private synchronized void readDictionary() throws EvioException {
+
+ if (evioVersion < 4) {
+ throw new EvioException("Unsupported version (" + evioVersion + ")");
+ }
+
+ // How many bytes remain in this block?
+ int bytesRemaining = blockBytesRemaining();
+ if (bytesRemaining < 12) {
+ throw new EvioException("Not enough data in first block");
+ }
+
+ // Once here, we are assured the entire next event is in this block.
+ int length = byteBuffer.getInt();
+ if (length < 1) {
+ throw new EvioException("Bad value for dictionary length (too big for java?)");
+ }
+ bytesRemaining -= 4;
+
+ // Since we're only interested in length, read but ignore rest of the header.
+ byteBuffer.getInt();
+ bytesRemaining -= 4;
+
+ // get the raw data
+ int eventDataSizeBytes = 4*(length - 1);
+ if (bytesRemaining < eventDataSizeBytes) {
+ throw new EvioException("Not enough data in first block");
+ }
+
+ byte bytes[] = new byte[eventDataSizeBytes];
+
+ // Read in dictionary data
+ try {
+ byteBuffer.get(bytes, 0, eventDataSizeBytes);
+ }
+ catch (Exception e) {
+ throw new EvioException("Problems reading buffer");
+ }
+
+ // This is the very first event and must be a dictionary
+ String[] strs = BaseStructure.unpackRawBytesToStrings(bytes, 0);
+ if (strs == null) {
+ throw new EvioException("Data in bad format");
+ }
+ dictionaryXML = strs[0];
+ }
+
+
+ /**
+ * Get the event in the file/buffer at a given index (starting at 1).
+ * As useful as this sounds, most applications will probably call
+ * {@link #parseEvent(int)} instead, since it combines combines
+ * getting the event with parsing it.<p>
+ *
+ * @param index number of event desired, starting at 1, from beginning of file/buffer
+ * @return the event in the file at the given index or null if none
+ * @throws EvioException if failed read due to bad file/buffer format;
+ * if out of memory; if index < 1
+ */
+ public synchronized EvioEvent getEvent(int index) throws EvioException {
+
+ if (index < 1) {
+ throw new EvioException("index arg starts at 1");
+ }
+
+ if (index > eventPositions.size()) {
+ return null;
+ }
+
+ if (evioVersion < 4) {
+ return gotoEventNumber(index);
+ }
+
+ index--;
+
+ EvioEvent event = new EvioEvent();
+ BaseStructureHeader header = event.getHeader();
+
+ int eventDataSizeBytes = 0;
+ // Save the current position
+ int origPosition = byteBuffer.position();
+ // Go to the correct position for the desired event
+ int position = eventPositions.get(index);
+ byteBuffer.position(position);
+
+ // Read length of event
+ int length = byteBuffer.getInt();
+ if (length < 1) {
+ throw new EvioException("Bad file/buffer format");
+ }
+ header.setLength(length);
+
+ try {
+ // Read second header word
+ if (byteOrder == ByteOrder.BIG_ENDIAN) {
+ // Interested in bit pattern, not negative numbers
+ header.setTag(ByteDataTransformer.shortBitsToInt(byteBuffer.getShort()));
+ int dt = ByteDataTransformer.byteBitsToInt(byteBuffer.get());
+ int type = dt & 0x3f;
+ int padding = dt >>> 6;
+ // If only 7th bit set, that can only be the legacy tagsegment type
+ // with no padding information - convert it properly.
+ if (dt == 0x40) {
+ type = DataType.TAGSEGMENT.getValue();
+ padding = 0;
+ }
+ header.setDataType(type);
+ header.setPadding(padding);
+
+ // Once we know what the data type is, let the no-arg constructed
+ // event know what type it is holding so xml names are set correctly.
+ event.setXmlNames();
+ header.setNumber(ByteDataTransformer.byteBitsToInt(byteBuffer.get()));
+ }
+ else {
+ header.setNumber(ByteDataTransformer.byteBitsToInt(byteBuffer.get()));
+ int dt = ByteDataTransformer.byteBitsToInt(byteBuffer.get());
+ int type = dt & 0x3f;
+ int padding = dt >>> 6;
+ if (dt == 0x40) {
+ type = DataType.TAGSEGMENT.getValue();
+ padding = 0;
+ }
+ header.setDataType(type);
+ header.setPadding(padding);
+
+ event.setXmlNames();
+ header.setTag(ByteDataTransformer.shortBitsToInt(byteBuffer.getShort()));
+ }
+
+ // Read the raw data
+ eventDataSizeBytes = 4*(header.getLength() - 1);
+ byte bytes[] = new byte[eventDataSizeBytes];
+ byteBuffer.get(bytes, 0, eventDataSizeBytes);
+
+ event.setRawBytes(bytes);
+ event.setByteOrder(byteOrder);
+ event.setEventNumber(index + 1);
+
+ }
+ catch (OutOfMemoryError e) {
+ throw new EvioException("Out Of Memory: (event size = " + eventDataSizeBytes + ")", e);
+ }
+ catch (Exception e) {
+ throw new EvioException("Error", e);
+ }
+ finally {
+ // Reset the buffer position in all cases
+ byteBuffer.position(origPosition);
+ }
+
+ return event;
+ }
+
+
+ /**
+ * This is the workhorse method. It retrieves the desired event from the file/buffer,
+ * and then parses it SAX-like. It will drill down and uncover all structures
+ * (banks, segments, and tagsegments) and notify any interested listeners.
+ *
+ * @param index number of event desired, starting at 1, from beginning of file/buffer
+ * @return the parsed event at the given index or null if none
+ * @throws EvioException if failed read due to bad file/buffer format;
+ * if out of memory; if index < 1
+ */
+ public synchronized EvioEvent parseEvent(int index) throws EvioException {
+ EvioEvent event = getEvent(index);
+ if (event != null) parseEvent(event);
+ return event;
+ }
+
+
+ /**
+ * Get the next event in the file. As useful as this sounds, most applications will probably call
+ * {@link #parseNextEvent()} instead, since it combines combines getting the next
+ * event with parsing the next event.<p>
+ * In evio version 4, events no longer cross block boundaries. There are only one or more
+ * complete events in each block. No changes were made to this method from versions 1-3 in order
+ * to read the version 4 format as it is subset of versions 1-3 with variable block length.
+ *
+ * @return the next event in the file. On error it throws an EvioException.
+ * On end of file, it returns <code>null</code>.
+ * @throws EvioException if failed read due to bad buffer format
+ */
+ public synchronized EvioEvent nextEvent() throws EvioException {
+ EvioEvent event = new EvioEvent();
BaseStructureHeader header = event.getHeader();
// Are we at the top of the file?
@@ -540,7 +1052,6 @@
throw new EvioException("Failed reading block header in nextEvent.");
}
isFirstEvent = true;
-//System.out.println("nextEvent: BLOCK HEADER :\n" + blockHeader.toString());
}
[truncated at 1000 lines; 213 more skipped]
jevio-base/src/main/java/org/jlab/coda/jevio
diff -u -r1.4 -r1.5
--- EvioFileTest.java 20 Mar 2012 23:21:49 -0000 1.4
+++ EvioFileTest.java 3 Apr 2013 20:07:46 -0000 1.5
@@ -34,7 +34,7 @@
while (status == ReadStatus.SUCCESS) {
status = reader.nextBlockHeader();
if (status == ReadStatus.SUCCESS) {
- reader.position(reader.getCurrentBlockHeader().nextBufferStartingPosition());
+ reader.position((int)reader.getCurrentBlockHeader().nextBufferStartingPosition());
count++;
}
}
@@ -60,7 +60,7 @@
while (status == ReadStatus.SUCCESS) {
status = reader.nextBlockHeader();
if (status == ReadStatus.SUCCESS) {
- reader.position(reader.getCurrentBlockHeader().nextBufferStartingPosition());
+ reader.position((int)reader.getCurrentBlockHeader().nextBufferStartingPosition());
blockCount++;
}
}
jevio-base/src/main/java/org/jlab/coda/jevio
diff -u -r1.3 -r1.4
--- StructureFinder.java 28 Feb 2012 19:41:36 -0000 1.3
+++ StructureFinder.java 3 Apr 2013 20:07:46 -0000 1.4
@@ -1,11 +1,13 @@
package org.jlab.coda.jevio;
+import java.util.Enumeration;
import java.util.List;
+import java.util.Vector;
/**
* This is a set of convenient static methods used to find lists of structures
- * within an event that match certain criteria. For most part it uses the
- * <code>List<BaseStructure> getMatchingStructures(IEvioFilter)</code>
+ * within an event, bank, segment, or tagsegment that match certain criteria. For the most
+ * part it uses the <code>List<BaseStructure> getMatchingStructures(IEvioFilter)</code>
* method on the provided <code>EvioEvent</code> object by constructing the
* appropriate filter.
*
@@ -17,26 +19,27 @@
/**
* Collect all the structures in an event that pass a filter.
- * @param event the event being queried.
+ * @param structure the event/bank/seg/tagseg being queried.
* @param filter the filter that must be passed. If <code>null</code>, this will return all the structures.
* @return a collection of all structures that are accepted by a filter for the provided event.
*/
- public static List<BaseStructure> getMatchingStructures(EvioEvent event, IEvioFilter filter) {
- if (event == null) {
+ public static List<BaseStructure> getMatchingStructures(BaseStructure structure, IEvioFilter filter) {
+ if (structure == null) {
+System.out.println("getMatchingStructures: returning null list");
return null;
}
- return event.getMatchingStructures(filter);
+ return structure.getMatchingStructures(filter);
}
/**
* Collect all the banks in an event that match a provided tag and number in their header.
* Only Banks are returned, because only Banks have a number field.
- * @param event the event being queried.
+ * @param structure the event/bank/seg/tagseg being queried.
* @param tag the tag to match.
* @param number the number to match.
* @return a collection of all Banks that are accepted by a filter for the provided event.
*/
- public static List<BaseStructure> getMatchingBanks(EvioEvent event, final int tag, final int number) {
+ public static List<BaseStructure> getMatchingBanks(BaseStructure structure, final int tag, final int number) {
IEvioFilter filter = new IEvioFilter() {
@Override
public boolean accept(StructureType type, IEvioStructure struct) {
@@ -45,51 +48,53 @@
(number == struct.getHeader().number);
}
};
- return getMatchingStructures(event, filter);
+ return getMatchingStructures(structure, filter);
}
/**
* Collect all the structures in an event that match a provided tag in their header.
- * @param event the event being queried.
+ * @param structure the event/bank/seg/tagseg being queried.
* @param tag the tag to match.
* @return a collection of all structures that are accepted by a filter for the provided event.
*/
- public static List<BaseStructure> getMatchingStructures(EvioEvent event, final int tag) {
+ public static List<BaseStructure> getMatchingStructures(BaseStructure structure, final int tag) {
IEvioFilter filter = new IEvioFilter() {
@Override
public boolean accept(StructureType type, IEvioStructure struct) {
return (tag == struct.getHeader().tag);
}
};
- return getMatchingStructures(event, filter);
+ return getMatchingStructures(structure, filter);
}
/**
* Collect all the non-banks (i.e., Segments and TagSegments) in an event that match
* a provided tag in their header. No Banks are returned.
- * @param event the event being queried.
+ * @param structure the event/bank/seg/tagseg being queried.
* @param tag the tag to match.
* @return a collection of all non-bank structures that are accepted by a filter for the provided event.
*/
- public static List<BaseStructure> getMatchingNonBanks(EvioEvent event, final int tag) {
+ public static List<BaseStructure> getMatchingNonBanks(BaseStructure structure, final int tag) {
IEvioFilter filter = new IEvioFilter() {
@Override
public boolean accept(StructureType type, IEvioStructure struct) {
return (type != StructureType.BANK) && (tag == struct.getHeader().tag);
}
};
- return getMatchingStructures(event, filter);
+ return getMatchingStructures(structure, filter);
}
+
/**
- * Collect all structures in an event that match the given dictionary description.
- * @param event the event being queried.
- * @param description dictionary description of structures to be returned.
+ * Collect all structures in an event that match the given dictionary name.
+ *
+ * @param structure the event/bank/seg/tagseg being queried.
+ * @param name dictionary name of structures to be returned.
* @param dictionary dictionary to be used; if null, an existing global dictionary will be used.
- * @return a list of BaseStructures that have the given description
+ * @return a list of BaseStructures that have the given name
* @throws EvioException if no dictionary is defined
*/
- public static List<BaseStructure> getMatchingStructures(EvioEvent event, String description,
+ public static List<BaseStructure> getMatchingStructures(BaseStructure structure, String name,
INameProvider dictionary)
throws EvioException {
@@ -104,34 +109,166 @@
}
}
- // This IEvioFilter selects structures that match the given dictionary description.
+ // This IEvioFilter selects structures that match the given dictionary name
class myEvioFilter implements IEvioFilter {
- String description;
+ String name;
INameProvider dictionary;
boolean useGlobalDictionary;
- myEvioFilter(boolean useGlobalDictionary, String description, INameProvider dictionary) {
- this.description = description;
- this.dictionary = dictionary;
+ myEvioFilter(boolean useGlobalDictionary, String name, INameProvider dictionary) {
+ this.name = name;
+ this.dictionary = dictionary;
this.useGlobalDictionary = useGlobalDictionary;
}
- public boolean accept(StructureType structureType, IEvioStructure structure) {
- String desc;
+ public boolean accept(StructureType structureType, IEvioStructure struct) {
+ String dictName;
if (useGlobalDictionary) {
- desc = NameProvider.getName((BaseStructure)structure);
+ dictName = NameProvider.getName((BaseStructure)struct);
}
else {
- desc = dictionary.getName((BaseStructure)structure);
+ dictName = dictionary.getName((BaseStructure)struct);
}
- // if this child matches the description add it to the list
- return description.equals(desc);
+ // If this structure matches the name, add it to the list
+ return name.equals(dictName);
}
};
- myEvioFilter filter = new myEvioFilter(useGlobalDictionary, description, dictionary);
- return getMatchingStructures(event, filter);
+ myEvioFilter filter = new myEvioFilter(useGlobalDictionary, name, dictionary);
+ return getMatchingStructures(structure, filter);
}
+
+ /**
+ * Collect all structures in an event whose <b>parent</b> has the given dictionary name.
+ *
+ * @param structure the event/bank/seg/tagseg being queried.
+ * @param parentName dictionary name of parent of structures to be returned.
+ * @param dictionary dictionary to be used; if null, an existing global dictionary will be used.
+ * @return a list of BaseStructures whose parent has the given name
+ * @throws EvioException if no dictionary is defined
+ */
+ public static List<BaseStructure> getMatchingParent(BaseStructure structure, String parentName,
+ INameProvider dictionary)
+ throws EvioException {
+
+ boolean useGlobalDictionary = false;
+
+ if (dictionary == null) {
+ if (!NameProvider.isProviderSet()) {
+ throw new EvioException("Dictionary must be given as arg or defined globally in NameProvider");
+ }
+ else {
+ useGlobalDictionary = true;
+ }
+ }
+
+ // This IEvioFilter selects structures whose parent has the given dictionary name
+ class myEvioFilter implements IEvioFilter {
+ String name;
+ INameProvider dictionary;
+ boolean useGlobalDictionary;
+
+ myEvioFilter(boolean useGlobalDictionary, String name, INameProvider dictionary) {
+ this.name = name;
+ this.dictionary = dictionary;
+ this.useGlobalDictionary = useGlobalDictionary;
+ }
+
+ public boolean accept(StructureType structureType, IEvioStructure struct) {
+ String dictName;
+
+ BaseStructure parent = ((BaseStructure)struct).getParent();
+ if (parent == null) {
+ return false;
+ }
+
+ if (useGlobalDictionary) {
+ dictName = NameProvider.getName(parent);
+ }
+ else {
+ dictName = dictionary.getName(parent);
+ }
+
+ // If this parent matches the name, add it to the list
+ return name.equals(dictName);
+ }
+ };
+
+ myEvioFilter filter = new myEvioFilter(useGlobalDictionary, parentName, dictionary);
+ return getMatchingStructures(structure, filter);
+ }
+
+
+ /**
+ * Collect all structures in an event who has a <b>child</b> with the given dictionary name.
+ *
+ * @param structure the event/bank/seg/tagseg being queried.
+ * @param childName dictionary name of a child of structures to be returned.
+ * @param dictionary dictionary to be used; if null, an existing global dictionary will be used.
+ * @return a list of BaseStructures who has a child with the given name
+ * @throws EvioException if no dictionary is defined
+ */
+ public static List<BaseStructure> getMatchingChild(BaseStructure structure, String childName,
+ INameProvider dictionary)
+ throws EvioException {
+
+ boolean useGlobalDictionary = false;
+
+ if (dictionary == null) {
+ if (!NameProvider.isProviderSet()) {
+ throw new EvioException("Dictionary must be given as arg or defined globally in NameProvider");
+ }
+ else {
+ useGlobalDictionary = true;
+ }
+ }
+
+ // This IEvioFilter selects structures who have a child with the given dictionary name
+ class myEvioFilter implements IEvioFilter {
+ String name;
+ INameProvider dictionary;
+ boolean useGlobalDictionary;
+
+ myEvioFilter(boolean useGlobalDictionary, String name, INameProvider dictionary) {
+ this.name = name;
+ this.dictionary = dictionary;
+ this.useGlobalDictionary = useGlobalDictionary;
+ }
+
+ public boolean accept(StructureType structureType, IEvioStructure struct) {
+ String dictName;
+
+ Vector<BaseStructure> children = ((BaseStructure)struct).getChildren();
+ if (children == null || children.size() < 1) {
+ return false;
+ }
+
+ BaseStructure bStruct;
+ Enumeration<BaseStructure> enumeration = children.elements();
+ while (enumeration.hasMoreElements()) {
+ bStruct = enumeration.nextElement();
+ if (useGlobalDictionary) {
+ dictName = NameProvider.getName(bStruct);
+ }
+ else {
+ dictName = dictionary.getName(bStruct);
+ }
+
+ if (name.equals(dictName)) {
+ // If this child matches the name, add it to the list
+ return true;
+ }
+ }
+
+ return false;
+ }
+ };
+
+ myEvioFilter filter = new myEvioFilter(useGlobalDictionary, childName, dictionary);
+ return getMatchingStructures(structure, filter);
+ }
+
+
}
jevio-base/src/main/java/org/jlab/coda/jevio
diff -u -r1.4 -r1.5
--- EvioFile.java 8 Mar 2012 20:39:26 -0000 1.4
+++ EvioFile.java 3 Apr 2013 20:07:46 -0000 1.5
@@ -792,7 +792,7 @@
}
// note: getEventCount() does not include dictionary
eventCount += blockHeader4.getEventCount();
- mappedByteBuffer.position(blockHeader4.nextBufferStartingPosition());
+ mappedByteBuffer.position((int)blockHeader4.nextBufferStartingPosition());
}
}
else {
jevio-base/src/main/java/org/jlab/coda/jevio
diff -u -r1.4 -r1.5
--- EventParser.java 20 Mar 2012 23:21:49 -0000 1.4
+++ EventParser.java 3 Apr 2013 20:07:46 -0000 1.5
@@ -84,8 +84,6 @@
// if it is not a structure of structures, we are done. That will leave the raw bytes in
// the "leaf" structures (e.g., a bank of ints) --which will be interpreted by the various get data methods.
-//System.out.println("Structure = \n" + structure.toString());
-//System.out.println("Header = \n" + structure.getHeader());
DataType dataType = structure.getHeader().getDataType();
if (!dataType.isStructure()) {
// notify the listeners
@@ -188,7 +186,7 @@
}
// notify the listeners
notifyEvioListeners(evioEvent, structure);
- } // parseStructure
+ }
/**
* Create a bank header from the first eight bytes of the data array.
jevio-base/src/main/java/org/jlab/coda/jevio
diff -u -r1.4 -r1.5
--- Demo.java 20 Mar 2012 23:21:49 -0000 1.4
+++ Demo.java 3 Apr 2013 20:07:46 -0000 1.5
@@ -45,13 +45,12 @@
String testEventFile = cwd + File.separator + "testdata" + File.separator + "out.ev";
String testDictFile = cwd + File.separator + "testdata" + File.separator + "eviodict.xml";
- EvioReader reader = null;
try {
File file = new File(testEventFile);
System.out.println("ev file: " + testEventFile + " size: " + file.length());
- reader = new EvioReader(testEventFile);
+ EvioReader reader = new EvioReader(testEventFile);
// uncomment below to test a filter
// IEvioFilter myFilter = new IEvioFilter() {
@@ -87,16 +86,16 @@
e.printStackTrace();
System.exit(1);
}
- catch (IOException e) {
+ catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
}
- public void startEventParse(EvioEvent evioEvent) { }
+ public void startEventParse(BaseStructure evioEvent) { }
- public void endEventParse(EvioEvent evioEvent) { }
+ public void endEventParse(BaseStructure evioEvent) { }
/**
* This IEvioListener has received a structure as the result of an event being parsed.
@@ -105,7 +104,7 @@
* or TAGSEGMENT.
*/
@Override
- public void gotStructure(EvioEvent evioEvent, IEvioStructure structure) {
+ public void gotStructure(BaseStructure evioEvent, IEvioStructure structure) {
BaseStructureHeader header = structure.getHeader();
jevio-base/src/main/java/org/jlab/coda/jevio
diff -u -r1.3 -r1.4
--- EvioXMLDictionary.java 28 Feb 2012 19:41:36 -0000 1.3
+++ EvioXMLDictionary.java 3 Apr 2013 20:07:46 -0000 1.4
@@ -3,52 +3,196 @@
import java.io.File;
import java.io.IOException;
import java.io.ByteArrayInputStream;
-import java.util.Collections;
-import java.util.Vector;
+import java.io.StringWriter;
+import java.util.LinkedHashMap;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
+
+
/**
* This was developed to read the xml dictionary that Maurizio uses for GEMC.
* It implements INameProvider, just like all other dictionary readers.
+ *
+ * An assumption in the following class is that each unique tag/num pair
+ * corresponds to an equally unique name. In other words, 2 different
+ * tag/numm pairs cannot have the same name.
*
* @author heddle
- *
+ * @author timmer
*/
@SuppressWarnings("serial")
-public class EvioXMLDictionary extends Vector<EvioDictionaryEntry> implements INameProvider {
+public class EvioXMLDictionary implements INameProvider {
- /**
- * There is only one type of element. This is its strange name.
- */
- private static String ENTRY = "xmldumpDictEntry";
+ /** Element containing entire dictionary. */
+ private static String DICT_TOP_LEVEL = "xmlDict";
- /**
- * The "name" attribute name.
- */
+ /** There is only one type of element which directly defines an entry (strange name). */
+ private static String ENTRY = "xmldumpDictEntry";
+
+ /**
+ * New, alternate, shortened form of ENTRY.
+ * @since 4.0
+ */
+ private static String ENTRY_ALT = "dictEntry";
+
+ /**
+ * Hierarchical container element.
+ * @since 4.0
+ */
+ private static String ENTRY_BANK = "bank";
+
+ /**
+ * Hierarchical leaf element.
+ * @since 4.0
+ */
+ private static String ENTRY_LEAF = "leaf";
+
+ /**
+ * Description element.
+ * @since 4.0
+ */
+ private static String DESCRIPTION = "description";
+
+ /**
+ * The "format" attribute string.
+ * @since 4.0
+ */
+ private static String FORMAT = "format";
+
+ /**
+ * The "type" attribute string.
+ * @since 4.0
+ */
+ private static String TYPE = "type";
+
+ /** The "name" attribute string. */
private static String NAME = "name";
- /**
- * The "tag" attribute name.
- */
+ /** The "tag" attribute string. */
private static String TAG = "tag";
- /**
- * The "num" attribute name.
- */
- private static String NUM = "num";
+ /** The "num" attribute string. */
+ private static String NUM = "num";
+
+
+
+ /**
+ * The character used to separate hierarchical parts of names.
+ * @since 4.0
+ */
+ private String delimiter = ".";
/**
- * Create an EvioXMLDictionary. This is not a standard CODA dictionary,
- * but a format developed for use by GEMC.
+ * This is the heart of the dictionary in which a key is composed of a tag/num
+ * pair and its corresponding value is a name. Using a hashmap ensures entries
+ * are unique.
+ * @since 4.0
+ */
+ private LinkedHashMap<EntryData,String> dictMap = new LinkedHashMap<EntryData,String>(1000);
+
+ /**
+ * This is a hashmap in which the key is a name and the value is composed
+ * of a corresponding tag/num pair. It's the reverse of the dictMap mapping.
+ * @since 4.0
+ */
+ private LinkedHashMap<String,EntryData> reverseMap = new LinkedHashMap<String,EntryData>(1000);
+
+ /**
+ * Top level xml Node object of xml DOM representation of dictionary.
+ * @since 4.0
+ */
+ private Node topLevelDoc;
+
+
+ /**
+ * Class to facilitate use of entry data as a key or value in a hash table.
+ */
+ private final class EntryData {
+
+ private final Integer tag;
+ private final Integer num;
+
+ private DataType type;
+
+ private final String format;
+ private final String description;
+
+ public EntryData(int tag, int num) {
+ this(tag, num, null, null, null);
+ }
+
+ public EntryData(int tag, int num, String type) {
+ this(tag, num, type, null, null);
+ }
+
+ public EntryData(int tag, int num, String type, String description) {
+ this(tag, num, type, description, null);
+ }
+
+ public EntryData(int tag, int num, String type, String description, String format) {
+ this.tag = tag;
+ this.num = num;
+ this.format = format;
+ this.description = description;
+ this.type = null;
+ if (type != null) {
+ try {
+ this.type = DataType.valueOf(type.toUpperCase());
+ }
+ catch (IllegalArgumentException e) {
+ }
+ }
+ }
+
+ public final int hashCode() {
+ int hashTag = tag.hashCode();
+ int hashNum = num.hashCode();
+
+ return (hashTag + hashNum) * hashNum + hashTag;
+ }
+
+ // Objects equal eachother if tag & num are the same
+ public final boolean equals(Object other) {
+ if (other instanceof EntryData) {
+ EntryData otherPair = (EntryData) other;
+ return(tag.equals(otherPair.tag) && num.equals(otherPair.num));
+ }
+
+ return false;
+ }
+
+ public final String toString() {
+ if (type == null) {
+ return "(tag=" + tag + ",num =" + num + ")";
+ }
+ return "(tag=" + tag + ",num =" + num + ",type=" + type + ")";
+ }
+
+ public final int getTag() {return tag;}
+ public final int getNum() {return num;}
+ public final DataType getType() {return type;}
+ public final String getFormat() {return format;}
+ public final String getDescription() {return description;}
+ }
+
+ /**
+ * Create an EvioXMLDictionary from an xml file.
*
* @param file file containing xml.
*/
@@ -57,8 +201,17 @@
}
/**
- * Create an EvioXMLDictionary. This is not a standard CODA dictionary,
- * but a format developed for use by GEMC.
+ * Create an EvioXMLDictionary from an xml file.
+ *
+ * @param file file containing xml.
+ * @param delimiter character used to separate hierarchical parts of names.
+ */
+ public EvioXMLDictionary(File file, String delimiter) {
+ this(getDomObject(file), delimiter);
+ }
+
+ /**
+ * Create an EvioXMLDictionary from an xml string.
*
* @param xmlString string containing xml.
*/
@@ -67,80 +220,525 @@
}
/**
- * Create an EvioXMLDictionary. This is not a standard CODA dictionary,
- * but a format developed for use by GEMC.
- *
+ * Create an EvioXMLDictionary from an xml string.
+ *
+ * @param xmlString string containing xml.
+ * @param delimiter character used to separate hierarchical parts of names.
+ */
+ public EvioXMLDictionary(String xmlString, String delimiter) {
+ this(getDomObject(xmlString), delimiter);
+ }
+
+ /**
+ * Create an EvioXMLDictionary from an xml Document object.
+ *
* @param domDocument DOM object representing xml dictionary.
*/
public EvioXMLDictionary(Document domDocument) {
- super(100);
+ this(domDocument, null);
+ }
- // try to get the XML dom document
- if (domDocument != null) {
- NodeList list = domDocument.getElementsByTagName(ENTRY);
-
- if (list != null) {
- for (int index = 0; index < list.getLength(); index++) {
- Node node = list.item(index);
- if (node != null) {
- if (node.hasAttributes()) {
- String name = null;
- String tag = null;
- String num = null;
-
- NamedNodeMap map = node.getAttributes();
-
- // get the name
- Node nameNode = map.getNamedItem(NAME);
- if (nameNode != null) {
- name = nameNode.getNodeValue();
- }
-
- // get the tag
- Node tagNode = map.getNamedItem(TAG);
- if (tagNode != null) {
- tag = tagNode.getNodeValue();
- }
-
- // get the num
- Node numNode = map.getNamedItem(NUM);
- if (numNode != null) {
- num = numNode.getNodeValue();
- }
-
- add (new EvioDictionaryEntry(tag, num, name));
- } // end node has attributes
- } // end node not null
- } // end loop over list
- }
- }
-
- //sort
- Collections.sort(this);
-
-//System.out.println(this);
+ /**
+ * Create an EvioXMLDictionary from an xml Document object.
+ *
+ * @param domDocument DOM object representing xml dictionary.
+ * @param delimiter character used to separate hierarchical parts of names.
+ */
+ public EvioXMLDictionary(Document domDocument, String delimiter) {
+
+ if (domDocument == null) return;
+ if (delimiter != null) this.delimiter = delimiter;
+
+ // Find top level of dictionary inside xml file being parsed (case sensitive!)
+ NodeList list = domDocument.getElementsByTagName(DICT_TOP_LEVEL);
+
+ // If no dictionary, just return
+ if (list.getLength() < 1) return;
+
+ // Otherwise, pick the first one
+ Node topNode = list.item(0);
+ topLevelDoc = topNode;
+
+ // Look at all the children, if any
+ NodeList kidList = topNode.getChildNodes();
+ if (kidList.getLength() < 1) return;
+
+ int tag, num;
+ boolean badEntry;
+ String name, tagStr, numStr, type, format, description;
+
+ // Pick out elements that are both old & new direct entry elements
+ for (int index = 0; index < kidList.getLength(); index++) {
+ Node node = kidList.item(index);
+ if (node == null) continue;
+
+ // Only looking for "xmldumpDictEntry" and "dictEntry" nodes
+ if (!node.getNodeName().equalsIgnoreCase(ENTRY) &&
+ !node.getNodeName().equalsIgnoreCase(ENTRY_ALT)) {
+ continue;
+ }
+ if (node.hasAttributes()) {
+ tag = num = 0;
+ badEntry = false;
+ name = tagStr = type = format = description = null;
+
+ NamedNodeMap map = node.getAttributes();
+
+ // Get the name
+ Node attrNode = map.getNamedItem(NAME);
+ if (attrNode != null) {
+ name = attrNode.getNodeValue();
+ }
+
+ // Get the tag
+ attrNode = map.getNamedItem(TAG);
+ if (attrNode != null) {
+ tagStr = attrNode.getNodeValue();
+ try {
+ tag = Integer.parseInt(tagStr);
+ if (tag < 0) badEntry = true;
+ }
+ catch (NumberFormatException e) {
+ badEntry = true;
+ }
+ }
+
+ // Get the num
+ attrNode = map.getNamedItem(NUM);
+ if (attrNode != null) {
+ numStr = attrNode.getNodeValue();
+ try {
+ num = Integer.parseInt(numStr);
+ if (num < 0) badEntry = true;
+ }
+ catch (NumberFormatException e) {
+ badEntry = true;
+ }
+ }
+
+ // Get the type, if any
+ attrNode = map.getNamedItem(TYPE);
+ if (attrNode != null) {
+ type = attrNode.getNodeValue();
+ }
+
+ // Look for description node (xml element) as child of entry node
+ NodeList children = node.getChildNodes();
+ if (children.getLength() > 0) {
+
+ // Pick out first description element only
+ for (int i = 0; i < children.getLength(); i++) {
+ Node childNode = children.item(i);
+ if (childNode == null) continue;
+
+ // Only looking for "description" node
+ if (!childNode.getNodeName().equalsIgnoreCase(DESCRIPTION)) {
+ continue;
+ }
+
+ description = childNode.getTextContent();
+//System.out.println("FOUND DESCRIPTION: = " + description);
+
+ // See if there's a format attribute
+ if (childNode.hasAttributes()) {
+ map = childNode.getAttributes();
+
+ // Get the format
+ attrNode = map.getNamedItem(FORMAT);
+ if (attrNode != null) {
+ format = attrNode.getNodeValue();
+//System.out.println("FOUND FORMAT: = " + format);
+ }
+ }
+ break;
+ }
+ }
+
+ // Skip meaningless entries
+ if (name == null || tagStr == null || badEntry) {
+ System.out.println("IGNORING badly formatted dictionary entry: name = " + name);
+ continue;
+ }
+
+ // Transform tag/num pair into single object
+ EntryData key = new EntryData(tag, num, type, description, format);
+
+ // Only add to dictionary if both name and tag/num pair are unique
+ if (!dictMap.containsKey(key) && !reverseMap.containsKey(name)) {
+ dictMap.put(key, name);
+ reverseMap.put(name, key);
+ }
+ else {
+ System.out.println("IGNORING duplicate dictionary entry: name = " + name);
+ }
+ }
+ }
+
+ // Look at the (new) hierarchical entry elements,
+ // recursively, and add all existing entries.
+ addHierachicalDictEntries(kidList, null);
} // end Constructor
-
- /**
- * Returns the pretty name of some evio structure. Typically this involves
- * the use of the "tag" and, if present, "num" fields. There may also be a hierarchical
- * dependence.
- *
- * @param structure the structure to find the name of.
- * @return a descriptive name, e.g., "Edep".
- */
- @Override
- public String getName(BaseStructure structure) {
- for (EvioDictionaryEntry entry : this) {
- if (entry.match(structure)) {
- return entry.getDescription();
- }
- }
- return INameProvider.NO_NAME_STRING;
+
+
+ /**
+ * Takes a list of the children of an xml node, selects the new
+ * hierachical elements and converts them into a number of dictionary
+ * entries which are added to this object.
+ * This method acts recursively since any node may contain children.
+ *
+ * @param kidList a list of the children of an xml node.
+ */
+ private void addHierachicalDictEntries(NodeList kidList, String parentName) {
+
+ if (kidList == null || kidList.getLength() < 1) return;
+
+ int tag, num;
+ boolean isLeaf, badEntry;
+ String name, tagStr, numStr, type, format, description;
+
+ for (int i = 0; i < kidList.getLength(); i++) {
+
+ Node node = kidList.item(i);
+ if (node == null) continue;
+
+ isLeaf = node.getNodeName().equalsIgnoreCase(ENTRY_LEAF);
+
+ // Only looking for "bank" and "leaf" nodes
+ if (!node.getNodeName().equalsIgnoreCase(ENTRY_BANK) && !isLeaf) {
+ continue;
+ }
+
+ if (node.hasAttributes()) {
+ tag = num = 0;
+ badEntry = false;
+ name = tagStr = type = format = description = null;
+
+ NamedNodeMap map = node.getAttributes();
+
+ // Get the name
+ Node attrNode = map.getNamedItem(NAME);
+ if (attrNode != null) {
+ name = attrNode.getNodeValue();
+ }
+
+ // Get the tag
+ Node tagNode = map.getNamedItem(TAG);
+ if (tagNode != null) {
+ tagStr = tagNode.getNodeValue();
+ try {
+ tag = Integer.parseInt(tagStr);
+ if (tag < 0) badEntry = true;
+ }
+ catch (NumberFormatException e) {
+ badEntry = true;
+ }
+ }
+
+ // Get the num
+ Node numNode = map.getNamedItem(NUM);
+ if (numNode != null) {
+ numStr = numNode.getNodeValue();
+ try {
+ num = Integer.parseInt(numStr);
+ if (num < 0) badEntry = true;
+ }
+ catch (NumberFormatException e) {
+ badEntry = true;
+ }
+ }
+
+ // Get the type, if any
+ attrNode = map.getNamedItem(TYPE);
+ if (attrNode != null) {
+ type = attrNode.getNodeValue();
+ }
+
+ // Look for description node (xml element) as child of entry node
+ NodeList children = node.getChildNodes();
+ if (children.getLength() > 0) {
+
+ // Pick out first description element only
+ for (int j = 0; j < children.getLength(); j++) {
+ Node childNode = children.item(j);
+ if (childNode == null) continue;
+
+ // Only looking for "description" node
+ if (!childNode.getNodeName().equalsIgnoreCase(DESCRIPTION)) {
+ continue;
+ }
+
+ description = childNode.getTextContent();
+//System.out.println("FOUND DESCRIPTION: = " + description);
+
+ // See if there's a format attribute
+ if (childNode.hasAttributes()) {
+ map = childNode.getAttributes();
+
+ // Get the format
+ attrNode = map.getNamedItem(FORMAT);
+ if (attrNode != null) {
+ format = attrNode.getNodeValue();
+//System.out.println("FOUND FORMAT: = " + format);
+ }
+ }
+ break;
+ }
+ }
+
+ // Skip meaningless entries
+ if (name == null || tagStr == null || badEntry) {
+ System.out.println("IGNORING badly formatted dictionary entry: name = " + name);
+ continue;
+ }
+
+ // Create hierarchical name
+ if (parentName != null) name = parentName + delimiter + name;
+
+ // Transform tag/num pair into single object
+ EntryData key = new EntryData(tag, num, type, description, format);
+
+ // Only add to dictionary if both name and tag/num pair are unique
+ if (!dictMap.containsKey(key) && !reverseMap.containsKey(name)) {
+ dictMap.put(key, name);
+ reverseMap.put(name, key);
+
+ // Look at this node's children recursively but skip a leaf's kids
+ if (!isLeaf) {
+ addHierachicalDictEntries(node.getChildNodes(), name);
+ }
+ else if (node.hasChildNodes()) {
+ System.out.println("IGNORING children of \"leaf\" element " + name);
+ }
+ }
+ else {
+ System.out.println("IGNORING duplicate dictionary entry: name = " + name);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Returns the name of a given evio structure.
+ *
+ * @param structure the structure to find the name of.
+ * @return a descriptive name or ??? if none found
+ */
+ @Override
+ public String getName(BaseStructure structure) {
+ int tag = structure.getHeader().getTag();
+ int num = structure.getHeader().getNumber();
+ return getName(tag, num);
+ }
+
+
+ /**
+ * Returns the name associated with the given tag and num.
+ *
+ * @param tag to find the name of
+ * @param num to find the name of
+ * @return a descriptive name or ??? if none found
+ */
+ public String getName(int tag, int num) {
+ EntryData key = new EntryData(tag, num);
+
+ String name = dictMap.get(key);
+ if (name == null) return INameProvider.NO_NAME_STRING;
+ return name;
+ }
+
+
+ /**
+ * Returns the description, if any, associated with the given tag and num.
+ *
+ * @param tag to find the description of
+ * @param num to find the description of
+ * @return the description or null if none found
+ */
+ public String getDescription(int tag, int num) {
+ // Need to find the description associated with given tag/num.
+ // First create an EntryData object to get associated name.
+ // (This works because of overriding the equals() method).
+ EntryData key = new EntryData(tag, num);
+
+ String name = dictMap.get(key);
+ if (name == null) {
+ System.out.println("getDescription: no entry for that key (tag/num)");
+ return null;
+ }
+
+ // Now that we have the name, use that to find the
+ // original EntryData object with the description in it.
+ EntryData origKey = reverseMap.get(name);
+ if (origKey == null) {
+ System.out.println("getDescription: no orig entry for that key (tag/num)");
+ return null;
+ }
+
+ return origKey.getDescription();
+ }
+
+
+ /**
+ * Returns the description, if any, associated with the name of a dictionary entry.
+ *
+ * @param name dictionary name
+ * @return description; null if name or is unknown or no description is associated with it
+ */
+ public String getDescription(String name) {
+ EntryData entry = reverseMap.get(name);
+ if (entry == null) {
+ return null;
+ }
+
+ return entry.getDescription();
+ }
+
+
+ /**
+ * Returns the format, if any, associated with the given tag and num.
+ *
+ * @param tag to find the format of
+ * @param num to find the format of
+ * @return the format or null if none found
+ */
+ public String getFormat(int tag, int num) {
+ // Need to find the format associated with given tag/num.
+ // First create an EntryData object to get associated name.
+ // (This works because of overriding the equals() method).
+ EntryData key = new EntryData(tag, num);
+
+ String name = dictMap.get(key);
+ if (name == null) return null;
+
+ // Now that we have the name, use that to find the
+ // original EntryData object with the format in it.
+ EntryData origKey = reverseMap.get(name);
+ if (origKey == null) {
+ return null;
+ }
+
+ return origKey.getFormat();
}
+
+ /**
+ * Returns the format, if any, associated with the name of a dictionary entry.
+ *
+ * @param name dictionary name
+ * @return format; null if name or is unknown or no format is associated with it
+ */
+ public String getFormat(String name) {
+ EntryData data = reverseMap.get(name);
+ if (data == null) {
+ return null;
+ }
+
+ return data.getFormat();
+ }
+
+
+
+ /**
+ * Returns the type, if any, associated with the given tag and num.
+ *
+ * @param tag to find the type of
+ * @param num to find the type of
+ * @return the type or null if none found
+ */
+ public DataType getType(int tag, int num) {
+ // Need to find the type associated with given tag/num.
+ // First create an EntryData object to get associated name.
+ // (This works because of overriding the equals() method).
+ EntryData key = new EntryData(tag, num);
+
+ String name = dictMap.get(key);
+ if (name == null) return null;
+
+ // Now that we have the name, use that to find the
+ // original EntryData object with the type in it.
+ EntryData origKey = reverseMap.get(name);
+ if (origKey == null) {
+ return null;
+ }
+
+ return origKey.getType();
+ }
+
+
+ /**
+ * Returns the type, if any, associated with the name of a dictionary entry.
+ *
+ * @param name dictionary name
+ * @return type; null if name or is unknown or no type is associated with it
+ */
+ public DataType getType(String name) {
+ EntryData data = reverseMap.get(name);
+ if (data == null) {
+ return null;
+ }
+
+ return data.getType();
+ }
+
+
+
+
+ /**
+ * Returns the tag/num pair, in an int array,
+ * corresponding to the name of a dictionary entry.
+ *
+ * @param name dictionary name
+ * @return an integer array in which the first element is the tag
+ * and the second is the num; null if name is unknown
+ */
+ public int[] getTagNum(String name) {
+ // Get the tag/num pair
+ EntryData pair = reverseMap.get(name);
+ if (pair == null) {
+ return null;
+ }
+
+ return new int[] {pair.getTag(), pair.getNum()};
+ }
+
+
+ /**
+ * Returns the tag corresponding to the name of a dictionary entry.
+ *
+ * @param name dictionary name
+ * @return tag; -1 if name unknown
+ */
+ public int getTag(String name) {
+ // Get the data
+ EntryData data = reverseMap.get(name);
+ if (data == null) {
+ return -1;
+ }
+
+ return data.getTag();
+ }
+
+
+ /**
+ * Returns the num corresponding to the name of a dictionary entry.
+ *
+ * @param name dictionary name
+ * @return num; -1 if name unknown
+ */
+ public int getNum(String name) {
+ // Get the data
+ EntryData data = reverseMap.get(name);
+ if (data == null) {
+ return -1;
+ }
+
+ return data.getNum();
+ }
+
+
/**
* Return a dom object corresponding to an xml file.
*
@@ -153,13 +751,11 @@
Document dom = null;
try {
-
// Using factory get an instance of document builder
DocumentBuilder db = dbf.newDocumentBuilder();
// parse using builder to get DOM representation of the XML file
dom = db.parse(file);
-
}
catch (ParserConfigurationException e) {
e.printStackTrace();
@@ -174,6 +770,7 @@
return dom;
}
+
/**
* Return a dom object corresponding to an xml string.
*
@@ -186,14 +783,12 @@
Document dom = null;
try {
-
// Using factory get an instance of document builder
DocumentBuilder db = dbf.newDocumentBuilder();
// parse using builder to get DOM representation of the XML string
ByteArrayInputStream bais = new ByteArrayInputStream(xmlString.getBytes());
dom = db.parse(bais);
-
}
catch (ParserConfigurationException e) {
e.printStackTrace();
@@ -208,32 +803,51 @@
return dom;
}
+
/**
* Get an xml representation of the dictionary.
* @return an xml representation of the dictionary.
*/
public String toXML() {
- StringBuilder sb = new StringBuilder(4096);
- sb.append("<!-- Evio Dictionary: mapping string to tag & num values -->\n");
- sb.append("<xmlDict>\n\n");
- for (EvioDictionaryEntry entry : this) {
- sb.append(entry.toXML());
- }
- sb.append("\n</xmlDict>\n");
- return sb.toString();
+ return nodeToString(topLevelDoc);
}
+
+ /**
+ * This method converts an xml Node object into an xml string.
+ * @return an xml representation of an xml Node object; null if conversion problems
+ */
+ private String nodeToString(Node node) {
+ StringWriter sw = new StringWriter();
+ try {
+ TransformerFactory transformerFactory = TransformerFactory.newInstance();
+ transformerFactory.setAttribute("indent-number", 3);
+ Transformer t = transformerFactory.newTransformer();
+ t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
+ t.setOutputProperty(OutputKeys.INDENT, "yes");
+ t.transform(new DOMSource(node), new StreamResult(sw));
+ } catch (TransformerException te) {
+ return null;
+ }
+ return sw.toString();
+ }
+
+
/**
* Get a string representation of the dictionary.
* @return a string representation of the dictionary.
*/
@Override
public String toString() {
+ EntryData pair;
StringBuilder sb = new StringBuilder(4096);
sb.append("-- Dictionary --\n");
- for (EvioDictionaryEntry entry : this) {
- sb.append(entry.toString());
- sb.append("\n");
+
+ for (String name : reverseMap.keySet()) {
+ // Get the tag/num pair
+ pair = reverseMap.get(name);
+ sb.append(String.format("tag: %-15s num: %-15s name: %s\n", pair.tag, pair.num, name));
+
}
return sb.toString();
}
jevio-base/src/main/java/org/jlab/coda/jevio
diff -u -r1.1 -r1.2
--- StructureTransformer.java 28 Feb 2012 19:41:36 -0000 1.1
+++ StructureTransformer.java 3 Apr 2013 20:07:46 -0000 1.2
@@ -19,7 +19,7 @@
* @param num num of the created EvioBank.
* @return the created EvioBank.
*/
- public EvioBank transform(EvioSegment segment, int num) {
+ static public EvioBank transform(EvioSegment segment, int num) {
BaseStructureHeader header = segment.getHeader();
DataType type = header.getDataType();
@@ -28,7 +28,7 @@
bankHeader.setPadding(header.getPadding());
EvioBank bank = new EvioBank(bankHeader);
- bank.copy(segment);
+ bank.transform(segment);
return bank;
}
@@ -48,7 +48,7 @@
* @param num num of the created EvioBank.
* @return the created EvioBank.
*/
- public EvioBank transform(EvioTagSegment tagsegment, int num) {
+ static public EvioBank transform(EvioTagSegment tagsegment, int num) {
BaseStructureHeader header = tagsegment.getHeader();
DataType type = header.getDataType();
@@ -57,7 +57,7 @@
bankHeader.setPadding(header.getPadding());
EvioBank bank = new EvioBank(bankHeader);
- bank.copy(tagsegment);
+ bank.transform(tagsegment);
return bank;
}
@@ -75,7 +75,7 @@
* @param segment EvioSegment object to transform.
* @return the created EvioTagSegment.
*/
- public EvioTagSegment transform(EvioSegment segment) {
+ static public EvioTagSegment transform(EvioSegment segment) {
BaseStructureHeader segHeader = segment.getHeader();
DataType type;
DataType segType = type = segHeader.getDataType();
@@ -94,7 +94,7 @@
tagsegHeader.setLength(segHeader.getLength());
EvioTagSegment tagseg = new EvioTagSegment(tagsegHeader);
- tagseg.copy(segment);
+ tagseg.transform(segment);
return tagseg;
}
@@ -114,7 +114,7 @@
* @param tagsegment EvioTagSegment object to transform.
* @return the created EvioSegment.
*/
- public EvioSegment transform(EvioTagSegment tagsegment) {
+ static public EvioSegment transform(EvioTagSegment tagsegment) {
BaseStructureHeader tagsegHeader = tagsegment.getHeader();
DataType type = tagsegHeader.getDataType();
@@ -125,7 +125,7 @@
segHeader.setPadding(tagsegHeader.getPadding());
EvioSegment seg = new EvioSegment(segHeader);
- seg.copy(tagsegment);
+ seg.transform(tagsegment);
return seg;
}
@@ -145,7 +145,7 @@
* @return the created EvioSegment.
* @throws EvioException if the bank is too long to change into a segment
*/
- public EvioSegment transform(EvioBank bank) throws EvioException {
+ static public EvioSegment transform(EvioBank bank) throws EvioException {
BaseStructureHeader header = bank.getHeader();
if (header.getLength() > 65535) {
throw new EvioException("Bank is too long to transform into segment");
@@ -158,7 +158,7 @@
segHeader.setPadding(header.getPadding());
EvioSegment seg = new EvioSegment(segHeader);
- seg.copy(bank);
+ seg.transform(bank);
return seg;
}
@@ -179,7 +179,7 @@
* @return the created EvioTagSegment.
* @throws EvioException if the bank is too long to change into a tagsegment
*/
- public EvioTagSegment transform(EvioBank bank, int dummy) throws EvioException {
+ static public EvioTagSegment transform(EvioBank bank, int dummy) throws EvioException {
BaseStructureHeader header = bank.getHeader();
if (header.getLength() > 65535) {
throw new EvioException("Bank is too long to transform into tagsegment");
@@ -202,7 +202,7 @@
tagsegHeader.setPadding(header.getPadding());
EvioTagSegment tagseg = new EvioTagSegment(tagsegHeader);
- tagseg.copy(bank);
+ tagseg.transform(bank);
return tagseg;
}
}
jevio-base/src/main/java/org/jlab/coda/jevio
diff -u -r1.3 -r1.4
--- EventBuilder.java 28 Feb 2012 19:41:36 -0000 1.3
+++ EventBuilder.java 3 Apr 2013 20:07:46 -0000 1.4
@@ -43,10 +43,16 @@
/**
* This goes through the event recursively, and makes sure all the length fields
* in the headers are properly set.
+ * @throws EvioException if the length of containing event is too large (> Integer.MAX_VALULE),
*/
public void setAllHeaderLengths() {
- event.setAllHeaderLengths();
- }
+ try {
+ event.setAllHeaderLengths();
+ }
+ catch (EvioException e) {
+ e.printStackTrace();
+ }
+ }
/**
* This clears all the data fields in a structure, but not the parent or the children. This keeps the
@@ -75,13 +81,13 @@
*
* @param parent the parent structure.
* @param child the child structure.
- * @throws EvioException if child is null, has wrong byte order,
+ * @throws EvioException if parent or child is null, child has wrong byte order,
* is wrong structure type, or parent is not a container
*/
public void addChild(BaseStructure parent, BaseStructure child) throws EvioException {
- if (child == null) {
- throw new EvioException("Attempt to add null child in addChild.");
+ if (child == null || parent == null) {
+ throw new EvioException("Null child or parent arg.");
}
if (child.getByteOrder() != event.getByteOrder()) {
@@ -127,6 +133,7 @@
parent.insert(child);
+ child.setParent(parent);
setAllHeaderLengths();
}
@@ -148,24 +155,139 @@
}
child.removeFromParent();
+ child.setParent(null);
setAllHeaderLengths();
}
-
+
+
/**
- * Appends int data to the structure. If the structure has no data, then this
- * is the same as setting the data.
- * @param structure the structure to receive the data, which is appended.
- * @param data the int data to append, or set if there is no existing data.
- * @throws EvioException
+ * Set int data to the structure. If the structure has data, it is overwritten
+ * even if the existing data is of a different type.
+ * @param structure the structure to receive the data.
+ * @param data the int data to write.
+ * @throws EvioException if structure arg is null
*/
- public void appendIntData(BaseStructure structure, int data[]) throws EvioException {
+ public void setIntData(BaseStructure structure, int data[]) throws EvioException {
if (structure == null) {
- throw new EvioException("Tried to append int data to a null structure.");
+ throw new EvioException("Tried to set int data to a null structure.");
}
- structure.appendIntData(data);
+ structure.setIntData(data);
setAllHeaderLengths();
}
-
+
+
+ /**
+ * Set short data to the structure. If the structure has data, it is overwritten
+ * even if the existing data is of a different type.
+ * @param structure the structure to receive the data.
+ * @param data the short data to write.
+ * @throws EvioException if structure arg is null
+ */
+ public void setShortData(BaseStructure structure, short data[]) throws EvioException {
+ if (structure == null) {
+ throw new EvioException("Tried to set short data to a null structure.");
+ }
+ structure.setShortData(data);
+ setAllHeaderLengths();
+ }
+
+
+ /**
+ * Set long data to the structure. If the structure has data, it is overwritten
+ * even if the existing data is of a different type.
+ * @param structure the structure to receive the data.
+ * @param data the long data to write.
+ * @throws EvioException if structure arg is null
+ */
+ public void setLongData(BaseStructure structure, long data[]) throws EvioException {
+ if (structure == null) {
+ throw new EvioException("Tried to set long data to a null structure.");
+ }
+ structure.setLongData(data);
+ setAllHeaderLengths();
+ }
+
+
+ /**
+ * Set byte data to the structure. If the structure has data, it is overwritten
+ * even if the existing data is of a different type.
+ * @param structure the structure to receive the data.
+ * @param data the byte data to write.
+ * @throws EvioException if structure arg is null
+ */
+ public void setByteData(BaseStructure structure, byte data[]) throws EvioException {
+ if (structure == null) {
+ throw new EvioException("Tried to set byte data to a null structure.");
+ }
+ structure.setByteData(data);
+ setAllHeaderLengths();
+ }
+
+
+ /**
+ * Appends int data to the structure. If the structure has no data, then this
+ * is the same as setting the data.
+ * @param structure the structure to receive the data, which is appended.
+ * @param data the int data to append, or set if there is no existing data.
+ * @throws EvioException
+ */
+ public void appendIntData(BaseStructure structure, int data[]) throws EvioException {
+ if (structure == null) {
+ throw new EvioException("Tried to append int data to a null structure.");
+ }
+ structure.appendIntData(data);
+ setAllHeaderLengths();
+ }
+
+
+ /**
+ * Set float data to the structure. If the structure has data, it is overwritten
+ * even if the existing data is of a different type.
+ * @param structure the structure to receive the data.
+ * @param data the float data to write.
+ * @throws EvioException if structure arg is null
+ */
+ public void setFloatData(BaseStructure structure, float data[]) throws EvioException {
+ if (structure == null) {
+ throw new EvioException("Tried to set float data to a null structure.");
+ }
+ structure.setFloatData(data);
+ setAllHeaderLengths();
+ }
+
+
+ /**
+ * Set double data to the structure. If the structure has data, it is overwritten
+ * even if the existing data is of a different type.
+ * @param structure the structure to receive the data.
+ * @param data the double data to write.
+ * @throws EvioException if structure arg is null
+ */
+ public void setIntData(BaseStructure structure, double data[]) throws EvioException {
+ if (structure == null) {
+ throw new EvioException("Tried to set double data to a null structure.");
+ }
+ structure.setDoubleData(data);
+ setAllHeaderLengths();
+ }
+
+
+ /**
+ * Set string data to the structure. If the structure has data, it is overwritten
+ * even if the existing data is of a different type.
+ * @param structure the structure to receive the data.
+ * @param data the string data to write.
+ * @throws EvioException if structure arg is null
+ */
+ public void setStringData(BaseStructure structure, String data[]) throws EvioException {
+ if (structure == null) {
+ throw new EvioException("Tried to set string data to a null structure.");
+ }
+ structure.setStringData(data);
+ setAllHeaderLengths();
+ }
+
+
/**
* Appends short data to the structure. If the structure has no data, then this
* is the same as setting the data.
@@ -181,7 +303,7 @@
setAllHeaderLengths();
}
-
+
/**
* Appends long data to the structure. If the structure has no data, then this
* is the same as setting the data.
@@ -196,7 +318,8 @@
structure.appendLongData(data);
setAllHeaderLengths();
}
-
+
+
/**
* Appends byte data to the structure. If the structure has no data, then this
* is the same as setting the data.
@@ -211,7 +334,8 @@
structure.appendByteData(data);
setAllHeaderLengths();
}
-
+
+
/**
* Appends float data to the structure. If the structure has no data, then this
* is the same as setting the data.
@@ -227,21 +351,22 @@
setAllHeaderLengths();
}
-// /**
-// * Appends char data to the structure. If the structure has no data, then this
-// * is the same as setting the data.
-// * @param structure the structure to receive the data, which is appended.
-// * @param data the char data to append, or set if there is no existing data.
-// * @throws EvioException
-// */
-// public void appendCharData(BaseStructure structure, char data[]) throws EvioException {
-//
-// if (structure == null) {
-// throw new EvioException("Tried to append char data to a null structure.");
-// }
-// structure.appendCharData(data);
-// setAllHeaderLengths();
-// }
+
+ /**
+ * Appends double data to the structure. If the structure has no data, then this
+ * is the same as setting the data.
+ * @param structure the structure to receive the data, which is appended.
+ * @param data the double data to append, or set if there is no existing data.
+ * @throws EvioException
+ */
+ public void appendDoubleData(BaseStructure structure, double data[]) throws EvioException {
+ if (structure == null) {
+ throw new EvioException("Tried to append double data to a null structure.");
+ }
+ structure.appendDoubleData(data);
+ setAllHeaderLengths();
+ }
+
/**
* Appends string data to the structure. If the structure has no data, then this
@@ -261,21 +386,6 @@
/**
- * Appends double data to the structure. If the structure has no data, then this
- * is the same as setting the data.
- * @param structure the structure to receive the data, which is appended.
- * @param data the double data to append, or set if there is no existing data.
- * @throws EvioException
- */
- public void appendDoubleData(BaseStructure structure, double data[]) throws EvioException {
- if (structure == null) {
- throw new EvioException("Tried to append double data to a null structure.");
- }
- structure.appendDoubleData(data);
- setAllHeaderLengths();
- }
-
- /**
* Get the underlying event.
* @return the underlying event.
*/
jevio-base/src/main/java/org/jlab/coda/jevio
diff -u -r1.2 -r1.3
--- BlockHeaderV4.java 20 Mar 2012 23:21:49 -0000 1.2
+++ BlockHeaderV4.java 3 Apr 2013 20:07:46 -0000 1.3
@@ -39,7 +39,7 @@
*
*
* Block Length = number of ints in block (including this one).
- * Block Number = id number
+ * Block Number = id number (starting at 1)
* Header Length = number of ints in this header (8)
* Event Count = number of events in this block (always an integral #).
* NOTE: this value should not be used to parse the following
@@ -72,11 +72,40 @@
*/
public class BlockHeaderV4 implements IEvioWriter, IBlockHeader {
- /**
- * The maximum block size in 32 bit ints.
+ /** The minimum & expected block header size in 32 bit ints. */
+ public static final int HEADER_SIZE = 8;
+
+ /**
+ * The maximum block size in 32 bit ints.
* Don't allow more than 40MBytes per block.
- */
- public static final int MAX_BLOCK_SIZE = 10000000;
+ */
+ public static final int MAX_BLOCK_SIZE = 10000000;
+
+ /** Dictionary presence is 9th bit in version/info word */
+ public static final int EV_DICTIONARY_MASK = 0x100;
+
+ /** "Last block" is 10th bit in version/info word */
+ public static final int EV_LASTBLOCK_MASK = 0x200;
+
+ /** Position of word for size of block in 32-bit words. */
+ public static final int EV_BLOCKSIZE = 0;
+ /** Position of word for block number, starting at 0. */
+ public static final int EV_BLOCKNUM = 1;
+ /** Position of word for size of header in 32-bit words (=8). */
+ public static final int EV_HEADERSIZE = 2;
+ /** Position of word for number of events in block. */
+ public static final int EV_COUNT = 3;
+ /** Position of word for reserved. */
+ public static final int EV_RESERVED1 = 4;
+ /** Position of word for version of file format. */
+ public static final int EV_VERSION = 5;
+ /** Position of word for reserved. */
+ public static final int EV_RESERVED2 = 6;
+ /** Position of word for magic number for endianness tracking. */
+ public static final int EV_MAGIC = 7;
+
+
+
/** The block (physical record) size in 32 bit ints. */
private int size;
@@ -164,6 +193,32 @@
magicNumber = MAGIC_NUMBER;
}
+ /**
+ * This copy constructor creates an evio version 4 BlockHeader
+ * from another object of this class.
+ * @param blkHeader block header object to copy
+ */
+ public BlockHeaderV4(BlockHeaderV4 blkHeader) {
+ if (blkHeader == null) {
+ return;
+ }
+ size = blkHeader.size;
+ number = blkHeader.number;
+ headerLength = blkHeader.headerLength;
+ version = blkHeader.version;
+ eventCount = blkHeader.eventCount;
+ reserved1 = blkHeader.reserved1;
+ reserved2 = blkHeader.reserved2;
+ magicNumber = blkHeader.magicNumber;
+ bitInfo = (BitSet) blkHeader.bitInfo.clone();
+ bufferStartingPosition = blkHeader.bufferStartingPosition;
+ }
+
+ /*** {@inheritDoc} */
+ public Object clone() {
+ return new BlockHeaderV4(this);
+ }
+
/**
* Set the size of the block (physical record). Some trivial checking is done.
*
@@ -238,7 +293,7 @@
* param headerLength the new block header length. This should be 8.
*/
public void setHeaderLength(int headerLength) {
- if (headerLength != 8) {
+ if (headerLength != HEADER_SIZE) {
System.out.println("Warning: Block Header Length = " + headerLength);
}
this.headerLength = headerLength;
@@ -264,6 +319,16 @@
}
/**
+ * Does this integer indicate that there is an evio dictionary
+ * (assuming it's the header's sixth word)?
+ *
+ * @return <code>true</code> if this int's indicates an evio dictionary, else <code>false</code>
+ */
+ static public boolean hasDictionary(int i) {
+ return ((i & EV_DICTIONARY_MASK) > 0);
+ }
+
+ /**
* Is this block's first event is an evio dictionary?
*
* @return <code>true</code> if this block's first event is an evio dictionary, else <code>false</code>
@@ -283,6 +348,35 @@
}
/**
+ * Does this integer indicate that this is the last block
+ * (assuming it's the header's sixth word)?
+ *
+ * @return <code>true</code> if this int's indicates the last block, else <code>false</code>
+ */
+ static public boolean isLastBlock(int i) {
+ return ((i & EV_LASTBLOCK_MASK) > 0);
+ }
+
+ /**
+ * Set the bit in the given arg which indicates this is the last block.
+ * @param i integer in which to set the last-block bit
+ * @return arg with last-block bit set
+ */
+ static public int setLastBlockBit(int i) {
+ return (i |= EV_LASTBLOCK_MASK);
+ }
+
+ /**
+ * Clear the last-block bit in the given arg to indicate it is NOT the last block.
+ * @param i integer in which to clear the last-block bit
+ * @return arg with last-block bit cleared
+ */
+ static public int clearLastBlockBit(int i) {
+ return (i &= ~EV_LASTBLOCK_MASK);
+ }
+
+
+ /**
* Get the value of bits 2-5. If this header is coming from the ROC,
* it represents the type of event being sent.
*
@@ -334,15 +428,22 @@
}
/**
- * Sets the right bits int the sixth word corresponding to the given type.
+ * Sets the right bits in bit set (2-5 when starting at 0)
+ * to hold 4 bits of the given type value. Useful when
+ * generating a bitset for use with {@link EventWriter}
+ * constructor.
+ *
+ * @param bSet Bitset containing all bits to be set
* @param type event type as int
*/
- public void setEventType(int type) {
+ static public void setEventType(BitSet bSet, int type) {
if (type < 0) type = 0;
else if (type > 15) type = 15;
+ if (bSet.size() < 6) return;
+
for (int i=2; i < 6; i++) {
- bitInfo.set(i, ((type >>> i-2) & 0x1) > 0);
+ bSet.set(i, ((type >>> i-2) & 0x1) > 0);
}
}
@@ -391,29 +492,62 @@
* in the lowest 8 bits and the set in the upper 24 bits. The arg isDictionary
* is set in the 9th bit and isEnd is set in the 10th bit.
*
- * @param set Bitset containing all bits to be set
+ * @param bSet Bitset containing all bits to be set
* @param isDictionary does this block include an evio xml dictionary as the first event?
* @param isEnd is this the last block of a file or a buffer?
* @return generated sixth word of this header.
*/
- static public int generateSixthWord(BitSet set, boolean isDictionary, boolean isEnd) {
+ static public int generateSixthWord(BitSet bSet, boolean isDictionary, boolean isEnd) {
int v = 4; // version
- for (int i=0; i < set.length(); i++) {
+ for (int i=0; i < bSet.length(); i++) {
if (i > 23) {
break;
}
- if (set.get(i)) {
+ if (bSet.get(i)) {
v |= (0x1 << (8+i));
}
}
v = isDictionary ? (v | 0x100) : v;
v = isEnd ? (v | 0x200) : v;
+
return v;
}
/**
+ * Calculates the sixth word of this header which has the version number (4)
+ * in the lowest 8 bits and the set in the upper 24 bits. The arg isDictionary
+ * is set in the 9th bit and isEnd is set in the 10th bit. Four bits of an int
+ * (event type) are set in bits 10-13.
+ *
+ * @param bSet Bitset containing all bits to be set
+ * @param isDictionary does this block include an evio xml dictionary as the first event?
+ * @param isEnd is this the last block of a file or a buffer?
+ * @param eventType 4 bit type of events header is containing
+ * @return generated sixth word of this header.
+ */
+ static public int generateSixthWord(BitSet bSet, boolean isDictionary,
+ boolean isEnd, int eventType) {
+ int v = 4; // version
+
+ for (int i=0; i < bSet.length(); i++) {
+ if (i > 23) {
+ break;
+ }
+ if (bSet.get(i)) {
+ v |= (0x1 << (8+i));
+ }
+ }
+
+ v = isDictionary ? (v | 0x100) : v;
+ v = isEnd ? (v | 0x200) : v;
+ v |= ((eventType & 0xf) << 10);
+
+ return v;
+ }
+
+ /**
* Parses the argument into the bit info fields.
* This ignores the version in the lowest 8 bits.
*
@@ -434,6 +568,14 @@
}
/**
+ * Sets the value of first reserved word.
+ * @param reserved1 the value for first reserved word.
+ */
+ public void setReserved1(int reserved1) {
+ this.reserved1 = reserved1;
+ }
+
+ /**
* Get the second reserved word.
* @return the second reserved word.
*/
@@ -441,6 +583,14 @@
return reserved2;
}
+ /**
+ * Sets the value of second reserved word.
+ * @param reserved2 the value for second reserved word.
+ */
+ public void setReserved2(int reserved2) {
+ this.reserved2 = reserved2;
+ }
+
/**
* Get the magic number the block (physical record) header which should be 0xc0da0100.
*
@@ -479,7 +629,9 @@
sb.append(String.format("block size: %d\n", size));
sb.append(String.format("number: %d\n", number));
sb.append(String.format("headerLen: %d\n", headerLength));
- sb.append(String.format("event count: %d\n", eventCount));
+ sb.append(String.format("event count: %d\n", eventCount));
+ sb.append(String.format("reserved 1: %d\n", reserved1));
+ sb.append(String.format("bit info: %s\n", bitInfo));
sb.append(String.format("has dictionary: %b\n", hasDictionary()));
sb.append(String.format("version: %d\n", version));
sb.append(String.format("magicNumber: %8x\n", magicNumber));
@@ -546,7 +698,8 @@
/**
* Gives the bytes remaining in this block (physical record) given a buffer position. The position is an absolute
* position in a byte buffer. This assumes that the absolute position in <code>bufferStartingPosition</code> is
- * being maintained properly by the reader.
+ * being maintained properly by the reader. No block is longer than 2.1GB - 31 bits of length. This is for
+ * practical reasons - so a block can be read into a single byte array.
*
* @param position the absolute current position is a byte buffer.
* @return the number of bytes remaining in this block (physical record.)
@@ -558,7 +711,6 @@
}
int nextBufferStart = nextBufferStartingPosition();
-//System.out.println("bytesRemaining: position = " + position + ", next buffer start = " + nextBufferStart);
if (position > nextBufferStart) {
throw new EvioException("Provided position beyond buffer end position.");
}
jevio-base/src/main/java/org/jlab/coda/jevio
diff -u -r1.3 -r1.4
--- BaseStructureHeader.java 28 Feb 2012 19:41:35 -0000 1.3
+++ BaseStructureHeader.java 3 Apr 2013 20:07:46 -0000 1.4
@@ -9,7 +9,7 @@
* @author heddle
*
*/
-public abstract class BaseStructureHeader implements IEvioWriter {
+public abstract class BaseStructureHeader implements Cloneable, IEvioWriter {
/**
* The length of the structure in ints. This never includes the
@@ -20,7 +20,7 @@
/**
* The structure tag.
*/
- protected int tag = 0;
+ protected int tag;
/**
* The data type of the structure.
@@ -32,7 +32,7 @@
* Allowed value is 0, 1, 2, or 3 (0,2 for shorts and 0-3 for bytes)
* and is stored in the upper 2 bits of the dataType when written out.
*/
- protected int padding = 0;
+ protected int padding;
/**
* The number represents an unsigned byte. Only Banks have a number
@@ -68,6 +68,21 @@
}
+ /**
+ * Clone this object by passing responsibility to the Object class
+ * which creates a bitwise copy which is fine since all fields are
+ * ints or enums.
+ */
+ public Object clone() {
+ try {
+ return super.clone();
+ }
+ catch (CloneNotSupportedException e) {
+ return null;
+ }
+ }
+
+
/**
* Get the number. Only Banks have a number field in their header, so this is only relevant for Banks.
*
jevio-base/src/main/java/org/jlab/coda/jevio
diff -u -r1.3 -r1.4
--- BankHeader.java 28 Feb 2012 19:41:35 -0000 1.3
+++ BankHeader.java 3 Apr 2013 20:07:46 -0000 1.4
@@ -12,7 +12,7 @@
*
*/
public final class BankHeader extends BaseStructureHeader {
-
+
/**
* Null constructor.
*/
jevio-base/src/main/java/org/jlab/coda/jevio
diff -u -r1.6 -r1.7
--- BaseStructure.java 16 Aug 2012 22:36:58 -0000 1.6
+++ BaseStructure.java 3 Apr 2013 20:07:46 -0000 1.7
@@ -6,6 +6,7 @@
import java.io.StringWriter;
import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.TreeNode;
import javax.xml.stream.XMLStreamException;
@@ -24,7 +25,7 @@
* @author timmer - add byte order tracking, make setAllHeaderLengths more efficient
*
*/
-public abstract class BaseStructure implements IEvioStructure, MutableTreeNode, IEvioWriter {
+public abstract class BaseStructure implements Cloneable, IEvioStructure, MutableTreeNode, IEvioWriter {
/**
* Holds the header of the bank.
@@ -33,6 +34,8 @@
/**
* The raw data of the structure.
+ * Storing data as raw bytes limits the # of data elements to
+ * Integer.MAX_VALUE/size_of_data_type_in_bytes.
*/
protected byte rawBytes[];
@@ -74,7 +77,7 @@
/**
* Used if raw data should be interpreted as composite type.
*/
- protected CompositeData compositeData;
+ protected CompositeData[] compositeData;
/**
* Used if raw data should be interpreted as a string.
@@ -113,12 +116,12 @@
/**
* Name of this object as an XML element.
*/
- protected String xmlElementName;
+ protected String xmlElementName = "unknown"; //TODO: get rid of this init val
/**
* Name of this object's contents as an XML attribute if it is a structure type.
*/
- protected String xmlContentAttributeName;
+ protected String xmlContentAttributeName = "unknown32"; //TODO: get rid of this init val
/**
* Endianness of the raw data if appropriate,
@@ -163,14 +166,17 @@
}
/**
- * Copy all the data from another BaseStructure object.
- * Although data is copied, children are not copied in the
- * deep clone way, but only added to this structure.
- * Does <b>not</b> copy header data.
+ * This method does a partial copy and is designed to help convert
+ * between banks, segments,and tagsegments in the {@link StructureTransformer}
+ * class (hence the name "transfrom").
+ * It copies all the data from another BaseStructure object.
+ * Children are <b>not</b> copied in the deep clone way,
+ * but their references are added to this structure.
+ * It does <b>not</b> copy header data or the parent either.
*
* @param structure BaseStructure from which to copy data.
*/
- public void copy(BaseStructure structure) {
+ public void transform(BaseStructure structure) {
if (structure == null) return;
@@ -262,7 +268,11 @@
case COMPOSITE:
if (structure.compositeData != null) {
- compositeData = (CompositeData) structure.compositeData.clone();
+ int len = structure.compositeData.length;
+ compositeData = new CompositeData[len];
+ for (int i=0; i < len; i++) {
+ compositeData[i] = (CompositeData) structure.compositeData[i].clone();
+ }
}
break;
@@ -280,6 +290,135 @@
}
}
+// static public BaseStructure copy(BaseStructure structure) {
+// return (BaseStructure)structure.clone();
+// }
+
+ /**
+ * Clone this object. First call the Object class's clone() method
+ * which creates a bitwise copy. Then clone all the mutable objects
+ * so that this method does a safe (not deep) clone. This means all
+ * children get cloned as well. Remind me again why anyone would
+ * want to clone their bratty kids?
+ */
+ public Object clone() {
+ try {
+ // "bs" is a bitwise copy. Do a deep clone of all object references.
+ BaseStructure bs = (BaseStructure)super.clone();
+
+ // Clone the header
+ bs.header = (BaseStructureHeader) header.clone();
+
+ // Clone raw bytes
+ if (rawBytes != null) {
+ bs.rawBytes = rawBytes.clone();
+ }
+
+ // Clone data
+ switch (header.getDataType()) {
+ case SHORT16:
+ case USHORT16:
+ if (shortData != null) {
+ bs.shortData = shortData.clone();
+ }
+ break;
+
+ case INT32:
+ case UINT32:
+ if (intData != null) {
+ bs.intData = intData.clone();
+ }
+ break;
+
+ case LONG64:
+ case ULONG64:
+ if (longData != null) {
+ bs.longData = longData.clone();
+ }
+ break;
+
+ case FLOAT32:
+ if (floatData != null) {
+ bs.floatData = floatData.clone();
+ }
+ break;
+
+ case DOUBLE64:
+ if (doubleData != null) {
+ bs.doubleData = doubleData.clone();
+ }
+ break;
+
+ case CHAR8:
+ case UCHAR8:
+ if (charData != null) {
+ bs.charData = charData.clone();
+ }
+ break;
+
+ case CHARSTAR8:
+ if (stringsList != null) {
+ bs.stringsList = new LinkedList<String>();
+ bs.stringsList.addAll(stringsList);
+ bs.stringData = new StringBuffer(stringData);
+ bs.stringEnd = stringEnd;
+ }
+ break;
+
+ case COMPOSITE:
+ if (compositeData != null) {
+ int len = compositeData.length;
+ bs.compositeData = new CompositeData[len];
+ for (int i=0; i < len; i++) {
+ bs.compositeData[i] = (CompositeData) compositeData[i].clone();
+ }
+ }
+ break;
+
+ case ALSOBANK:
+ case ALSOSEGMENT:
+ case BANK:
+ case SEGMENT:
+ case TAGSEGMENT:
+ // Create kid storage since we're a container type
+ bs.children = new Vector<BaseStructure>(10);
+
+ // Clone kids
+ for (BaseStructure kid : children) {
+ bs.children.add((BaseStructure)kid.clone());
+ }
+ break;
+
+ default:
+ }
+
+ // Do NOT clone the parent, just keep it as a reference.
+
+ return bs;
+ }
+ catch (CloneNotSupportedException e) {
+ return null;
+ }
+ }
+
+ /** Clear all existing data from a non-container structure. */
+ private void clearData() {
+
+ if (header.getDataType().isStructure()) return;
+
+ rawBytes = null;
+ charData = null;
+ intData = null;
+ longData = null;
+ shortData = null;
+ doubleData = null;
+ floatData = null;
+ compositeData = null;
+ stringData = null;
+ stringsList = null;
+ stringEnd = 0;
+ numberDataItems = 0;
+ }
/**
* A convenience method use instead of "instanceof" to see what type of structure we have. Note: this returns the
@@ -561,7 +700,7 @@
// calculate the data length so we're OK returning
// any reasonable value here.
numberDataItems = 1;
- if (compositeData != null) numberDataItems = compositeData.getItems().size();
+ if (compositeData != null) numberDataItems = compositeData.length;
break;
default:
}
@@ -744,14 +883,15 @@
/**
* This is a method from the IEvioStructure Interface. Gets the composite data as
- * a CompositeData object, if the content type as indicated by the header is appropriate.<p>
+ * an array of CompositeData objects, if the content type as indicated by the header
+ * is appropriate.<p>
*
- * @return the data as a CompositeData object, or <code>null</code>
+ * @return the data as an array of CompositeData objects, or <code>null</code>
* if this makes no sense for the given content type.
* @throws EvioException if the data is internally inconsistent
*/
@Override
- public CompositeData getCompositeData() throws EvioException {
+ public CompositeData[] getCompositeData() throws EvioException {
switch (header.getDataType()) {
case COMPOSITE:
@@ -759,7 +899,7 @@
if (rawBytes == null) {
return null;
}
- compositeData = new CompositeData(rawBytes, byteOrder);
+ compositeData = CompositeData.parse(rawBytes, byteOrder);
}
return compositeData;
default:
@@ -984,8 +1124,9 @@
}
}
// Look for any non-printing/control characters (not including null)
- // and end the string there. Allow new lines (c = 10).
- else if ( (c < 32 || c == 127) && c != 10 ) {
+ // and end the string there. Allow whitespace.
+ else if ( (c < 32 || c == 127) && !Character.isWhitespace(c)) {
+//System.out.println(" found non-printing c = " + (int)c + " at i = " + i);
break;
}
}
@@ -1051,8 +1192,8 @@
}
}
// Look for any non-printing/control characters (not including null)
- // and end the string there. Allow new lines (c = 10).
- else if ( (c < 32 || c == 127) && c != 10 ) {
+ // and end the string there. Allow whitespace.
+ else if ( (c < 32 || c == 127) && !Character.isWhitespace(c)) {
//System.out.println(" found non-printing c = " + (int)c + " at i = " + i);
break;
}
@@ -1166,7 +1307,9 @@
*/
@Override
public void remove(int index) {
- children.remove(index); // TODO: what if children == null ?
+ if (children == null) return;
+ BaseStructure bs = children.remove(index);
+ bs.setParent(null);
if (children.size() < 1) isLeaf = true;
lengthsUpToDate(false);
}
@@ -1178,7 +1321,9 @@
*/
@Override
public void remove(MutableTreeNode child) {
+ if (children == null || child == null) return;
children.remove(child);
+ child.setParent(null);
if (children.size() < 1) isLeaf = true;
lengthsUpToDate(false);
}
@@ -1188,7 +1333,7 @@
*/
@Override
public void removeFromParent() {
- parent.remove(this);
+ if (parent != null) parent.remove(this);
}
/**
@@ -1199,6 +1344,92 @@
this.parent = (BaseStructure) parent;
}
+ /**
+ * Visit all the structures in this structure (including the structure itself --
+ * which is considered its own descendant).
+ * This is similar to listening to the event as it is being parsed,
+ * but is done to a complete (already) parsed event.
+ *
+ * @param listener an listener to notify as each structure is visited.
+ */
+ public void vistAllStructures(IEvioListener listener) {
+ visitAllDescendants(this, listener, null);
+ }
+
+ /**
+ * Visit all the structures in this structure (including the structure itself --
+ * which is considered its own descendant) in a depth first manner.
+ *
+ * @param listener an listener to notify as each structure is visited.
+ * @param filter an optional filter that must "accept" structures before
+ * they are passed to the listener. If <code>null</code>, all
+ * structures are passed. In this way, specific types of
+ * structures can be captured.
+ */
+ public void vistAllStructures(IEvioListener listener, IEvioFilter filter) {
+ visitAllDescendants(this, listener, filter);
+ }
+
+ /**
+ * Visit all the descendants of a given structure
+ * (which is considered a descendant of itself.)
+ *
+ * @param structure the starting structure.
+ * @param listener an listener to notify as each structure is visited.
+ * @param filter an optional filter that must "accept" structures before
+ * they are passed to the listener. If <code>null</code>, all
+ * structures are passed. In this way, specific types of
+ * structures can be captured.
+ */
+ private void visitAllDescendants(BaseStructure structure, IEvioListener listener, IEvioFilter filter) {
+ if (listener != null) {
+ boolean accept = true;
+ if (filter != null) {
+ accept = filter.accept(structure.getStructureType(), structure);
+ }
+
+ if (accept) {
+ listener.gotStructure(this, structure);
+ }
+ }
+
+ if (!(structure.isLeaf())) {
+ for (BaseStructure child : structure.getChildren()) {
+ visitAllDescendants(child, listener, filter);
+ }
+ }
+ }
+
+ /**
+ * Visit all the descendant structures, and collect those that pass a filter.
+ * @param filter the filter that must be passed. If <code>null</code>,
+ * this will return all the structures.
+ * @return a collection of all structures that are accepted by a filter.
+ */
+ public List<BaseStructure> getMatchingStructures(IEvioFilter filter) {
+ final Vector<BaseStructure> structures = new Vector<BaseStructure>(25, 10);
+
+ IEvioListener listener = new IEvioListener() {
+ @Override
+ public void startEventParse(BaseStructure structure) { }
+
+ @Override
+ public void endEventParse(BaseStructure structure) { }
+
+ @Override
+ public void gotStructure(BaseStructure topStructure, IEvioStructure structure) {
+ structures.add((BaseStructure)structure);
+ }
+ };
+
+ vistAllStructures(listener, filter);
+
+ if (structures.size() == 0) {
+ return null;
+ }
+ return structures;
+ }
+
/**
* This method is not relevant for this implementation.
* An empty implementation is provided to satisfy the interface.
@@ -1225,13 +1456,18 @@
* Obtain the child at the given index. Part of the <code>MutableTreeNode</code> interface.
*
* @param index the target index.
- * @return the child at the given index.
- * @throws ArrayIndexOutOfBoundsException if the index is out of range
- * ({@code index < 0 || index >= children.size()})
+ * @return the child at the given index or null if none
*/
@Override
public TreeNode getChildAt(int index) {
- return children.elementAt(index);
+ if (children == null) return null;
+
+ BaseStructure b = null;
+ try {
+ b = children.elementAt(index);
+ }
+ catch (ArrayIndexOutOfBoundsException e) { }
+ return b;
}
/**
@@ -1250,10 +1486,11 @@
/**
* Get the index of a node. Part of the <code>MutableTreeNode</code> interface.
*
- * @return the index of the target node.
+ * @return the index of the target node or -1 if no such node in tree
*/
@Override
public int getIndex(TreeNode node) {
+ if (children == null || node == null) return -1;
return children.indexOf(node);
}
@@ -1269,7 +1506,6 @@
@Override
public boolean isLeaf() {
return isLeaf;
- //return ((children == null) || (children.size() < 1));
}
/**
@@ -1441,7 +1677,9 @@
return;
}
}
- compositeData.toXML(xmlWriter, this, true);
+ for (int i=0; i < compositeData.length; i++) {
+ compositeData[i].toXML(xmlWriter, this, true);
+ }
break;
}
@@ -1554,7 +1792,7 @@
*
* @return the length that would go in the header field (for a leaf).
*/
- public int setAllHeaderLengths() {
+ public int setAllHeaderLengthsSemiOrig() throws EvioException {
// if length info is current, don't bother to recalculate it
if (lengthsUpToDate) {
return header.getLength();
@@ -1579,7 +1817,7 @@
}
}
- datalen += header.getHeaderLength() - 1; // extra header word for banks
+ datalen += header.getHeaderLength() - 1; // length header word
// set the datalen for the header
header.setLength(datalen);
@@ -1587,6 +1825,55 @@
return datalen;
}
+ /**
+ * Compute and set length of all header fields for this structure and all its descendants.
+ * For writing events, this will be crucial for setting the values in the headers.
+ *
+ * @return the length that would go in the header field (for a leaf).
+ * @throws EvioException if the length is too large (> {@link Integer#MAX_VALUE}),
+ */
+ public int setAllHeaderLengths() throws EvioException {
+ // if length info is current, don't bother to recalculate it
+ if (lengthsUpToDate) {
+ return header.getLength();
+ }
+
+ int datalen, len;
+
+ if (isLeaf()) {
+ // # of 32 bit ints for leaves, 0 for empty containers (also considered leaves)
+ datalen = dataLength();
+ }
+ else {
+ datalen = 0;
+
+ if (children == null) {
+System.err.println("Non leaf with null children!");
+ System.exit(1);
+ }
+ for (BaseStructure child : children) {
+ len = child.setAllHeaderLengths();
+ // Add this check to make sure structure is not being overfilled
+ if (Integer.MAX_VALUE - datalen < len) {
+ throw new EvioException("added data overflowed containing structure");
+ }
+ datalen += len + 1; // + 1 for the header length word of each child
+ }
+ }
+
+ len = header.getHeaderLength() - 1; // - 1 for length header word
+ if (Integer.MAX_VALUE - datalen < len) {
+ throw new EvioException("added data overflowed containing structure");
+ }
+
+ datalen += len;
+//System.out.println("len = " + datalen);
+ // set the datalen for the header
+ header.setLength(datalen);
+ lengthsUpToDate(true);
+ return datalen;
+ }
+
/**
* Write myself out a byte buffer with fastest algorithms I could find.
*
@@ -1595,13 +1882,15 @@
*/
public int write(ByteBuffer byteBuffer) {
- int curPos, startPos = byteBuffer.position();
+ int startPos = byteBuffer.position();
// write the header
header.write(byteBuffer);
+ int curPos = byteBuffer.position();
+
if (isLeaf()) {
- BaseStructureHeader header = getHeader();
+ //BaseStructureHeader header = getHeader();
switch (header.getDataType()) {
case DOUBLE64:
// if data sent over wire or read from file ...
@@ -1612,7 +1901,6 @@
}
// else if bytes need swapping ...
else {
- curPos = byteBuffer.position();
DoubleBuffer db = byteBuffer.asDoubleBuffer();
DoubleBuffer rawBuf = ByteBuffer.wrap(rawBytes).order(byteOrder).asDoubleBuffer();
db.put(rawBuf);
@@ -1621,7 +1909,6 @@
}
// else if user set data thru API (can't-rely-on / no rawBytes array) ...
else {
- curPos = byteBuffer.position();
DoubleBuffer db = byteBuffer.asDoubleBuffer();
db.put(doubleData, 0, doubleData.length);
byteBuffer.position(curPos + 8*doubleData.length);
@@ -1634,7 +1921,6 @@
byteBuffer.put(rawBytes, 0, rawBytes.length);
}
else {
- curPos = byteBuffer.position();
FloatBuffer db = byteBuffer.asFloatBuffer();
FloatBuffer rawBuf = ByteBuffer.wrap(rawBytes).order(byteOrder).asFloatBuffer();
db.put(rawBuf);
@@ -1642,7 +1928,6 @@
}
}
else {
- curPos = byteBuffer.position();
FloatBuffer db = byteBuffer.asFloatBuffer();
db.put(floatData, 0, floatData.length);
byteBuffer.position(curPos + 4*floatData.length);
@@ -1656,7 +1941,6 @@
byteBuffer.put(rawBytes, 0, rawBytes.length);
}
else {
- curPos = byteBuffer.position();
LongBuffer db = byteBuffer.asLongBuffer();
LongBuffer rawBuf = ByteBuffer.wrap(rawBytes).order(byteOrder).asLongBuffer();
db.put(rawBuf);
@@ -1664,7 +1948,6 @@
}
}
else {
- curPos = byteBuffer.position();
LongBuffer db = byteBuffer.asLongBuffer();
db.put(longData, 0, longData.length);
byteBuffer.position(curPos + 8*longData.length);
@@ -1679,7 +1962,6 @@
byteBuffer.put(rawBytes, 0, rawBytes.length);
}
else {
- curPos = byteBuffer.position();
IntBuffer db = byteBuffer.asIntBuffer();
IntBuffer rawBuf = ByteBuffer.wrap(rawBytes).order(byteOrder).asIntBuffer();
db.put(rawBuf);
@@ -1687,7 +1969,6 @@
}
}
else {
- curPos = byteBuffer.position();
IntBuffer db = byteBuffer.asIntBuffer();
db.put(intData, 0, intData.length);
byteBuffer.position(curPos + 4*intData.length);
@@ -1701,7 +1982,6 @@
byteBuffer.put(rawBytes, 0, rawBytes.length);
}
else {
- curPos = byteBuffer.position();
ShortBuffer db = byteBuffer.asShortBuffer();
ShortBuffer rawBuf = ByteBuffer.wrap(rawBytes).order(byteOrder).asShortBuffer();
db.put(rawBuf);
@@ -1709,7 +1989,6 @@
}
}
else {
- curPos = byteBuffer.position();
ShortBuffer db = byteBuffer.asShortBuffer();
db.put(shortData, 0, shortData.length);
byteBuffer.position(curPos + 2*shortData.length);
@@ -1747,6 +2026,11 @@
byteBuffer.put(rawBytes, 0, rawBytes.length);
}
else {
+// System.out.println("Before swap in writing output:");
+// int[] intA = ByteDataTransformer.getAsIntArray(rawBytes, ByteOrder.BIG_ENDIAN);
+// for (int i : intA) {
+// System.out.println("Ox" + Integer.toHexString(i));
+// }
// swap rawBytes
byte[] swappedRaw = new byte[rawBytes.length];
try {
@@ -1756,6 +2040,11 @@
catch (EvioException e) { /* never happen */ }
// write them to buffer
byteBuffer.put(swappedRaw, 0, swappedRaw.length);
+// System.out.println("Swap in writing output:");
+// intA = ByteDataTransformer.getAsIntArray(swappedRaw, ByteOrder.LITTLE_ENDIAN);
+// for (int i : intA) {
+// System.out.println("Ox" + Integer.toHexString(i));
+// }
}
}
break;
@@ -1764,7 +2053,7 @@
} // switch
} // isLeaf
- else {
+ else if (children != null) {
for (BaseStructure child : children) {
child.write(byteBuffer);
}
@@ -1773,36 +2062,48 @@
return byteBuffer.position() - startPos;
}
- /**
- * Appends int data to the structure. If the structure has no data, then this
- * is the same as setting the data.
- * @param data the int data to append, or set if there is no existing data.
- * @throws EvioException
- */
- public void appendIntData(int data[]) throws EvioException {
-
- // make sure the structure is set to hold this kind of data
- DataType dataType = header.getDataType();
- if ((dataType != DataType.INT32) && (dataType != DataType.UINT32)) {
- throw new EvioException("Tried to append int data to a structure of type: " + dataType);
- }
-
- // if no data to append, just cave
- if (data == null) {
- return;
- }
-
- // if no int data ...
- if (intData == null) {
- // if no raw data, things are easy
+
+ //----------------------------------------------------------------------
+ // Methods to append to exising data if any or to set the data if none.
+ //----------------------------------------------------------------------
+
+
+ /**
+ * Appends int data to the structure. If the structure has no data, then this
+ * is the same as setting the data.
+ * @param data the int data to append, or set if there is no existing data.
+ * @throws EvioException if adding data to a structure of a different data type;
+ * if data has too many elements to store in raw byte array (JVM limit)
+ */
+ public void appendIntData(int data[]) throws EvioException {
+
+ // make sure the structure is set to hold this kind of data
+ DataType dataType = header.getDataType();
+ if ((dataType != DataType.INT32) && (dataType != DataType.UINT32)) {
+ throw new EvioException("Tried to append int data to a structure of type: " + dataType);
+ }
+
+ // if no data to append, just cave
+ if (data == null) {
+ return;
+ }
+
+ // if no int data ...
+ if (intData == null) {
+ // if no raw data, things are easy
if (rawBytes == null) {
- intData = data;
+ intData = data;
numberDataItems = data.length;
}
// otherwise expand raw data first, then add int array
else {
int size1 = rawBytes.length/4;
int size2 = data.length;
+
+ // TODO: Will need to revise this if using Java 9+ with long array indeces
+ if (Integer.MAX_VALUE - size1 < size2) {
+ throw new EvioException("added data overflowed containing structure");
+ }
intData = new int[size1 + size2];
// unpack existing raw data
ByteDataTransformer.toIntArray(rawBytes, byteOrder, intData, 0);
@@ -1810,29 +2111,36 @@
System.arraycopy(data, 0, intData, size1, size2);
numberDataItems = size1 + size2;
}
- }
- else {
- int size1 = intData.length; // existing data
- int size2 = data.length; // new data to append
- intData = Arrays.copyOf(intData, size1 + size2); // extend existing array
- System.arraycopy(data, 0, intData, size1, size2); // append
+ }
+ else {
+ int size1 = intData.length; // existing data
+ int size2 = data.length; // new data to append
+
+ // TODO: Will need to revise this if using Java 9+ with long array indeces
+ if (Integer.MAX_VALUE - size1 < size2) {
+ throw new EvioException("added data overflowed containing structure");
+ }
+ intData = Arrays.copyOf(intData, size1 + size2); // extend existing array
+ System.arraycopy(data, 0, intData, size1, size2); // append
numberDataItems += size2;
- }
+ }
// This is not necessary but results in a tremendous performance
// boost (10x) when writing events over a high speed network. Allows
// the write to be a byte array copy.
+ // Storing data as raw bytes limits the # of elements to Integer.MAX_VALUE/4.
rawBytes = ByteDataTransformer.toBytes(intData, byteOrder);
lengthsUpToDate(false);
- setAllHeaderLengths();
- }
-
+ setAllHeaderLengths();
+ }
+
/**
* Appends short data to the structure. If the structure has no data, then this
* is the same as setting the data.
* @param data the short data to append, or set if there is no existing data.
- * @throws EvioException
+ * @throws EvioException if adding data to a structure of a different data type;
+ * if data has too many elements to store in raw byte array (JVM limit)
*/
public void appendShortData(short data[]) throws EvioException {
@@ -1853,6 +2161,10 @@
else {
int size1 = (rawBytes.length - header.getPadding())/2;
int size2 = data.length;
+
+ if (Integer.MAX_VALUE - size1 < size2) {
+ throw new EvioException("added data overflowed containing structure");
+ }
shortData = new short[size1 + size2];
ByteDataTransformer.toShortArray(rawBytes, header.getPadding(),
byteOrder, shortData, 0);
@@ -1863,6 +2175,10 @@
else {
int size1 = shortData.length;
int size2 = data.length;
+
+ if (Integer.MAX_VALUE - size1 < size2) {
+ throw new EvioException("added data overflowed containing structure");
+ }
shortData = Arrays.copyOf(shortData, size1 + size2);
System.arraycopy(data, 0, shortData, size1, size2);
numberDataItems += size2;
@@ -1872,6 +2188,10 @@
if (numberDataItems%2 != 0) {
header.setPadding(2);
+ if (Integer.MAX_VALUE - 2*numberDataItems < 2) {
+ throw new EvioException("added data overflowed containing structure");
+ }
+
// raw bytes must include padding
rawBytes = new byte[2*numberDataItems + 2];
ByteDataTransformer.toBytes(shortData, byteOrder, rawBytes, 0);
@@ -1890,7 +2210,8 @@
* Appends long data to the structure. If the structure has no data, then this
* is the same as setting the data.
* @param data the long data to append, or set if there is no existing data.
- * @throws EvioException
+ * @throws EvioException if adding data to a structure of a different data type;
+ * if data has too many elements to store in raw byte array (JVM limit)
*/
public void appendLongData(long data[]) throws EvioException {
@@ -1911,6 +2232,10 @@
else {
int size1 = rawBytes.length/8;
int size2 = data.length;
+
+ if (Integer.MAX_VALUE - size1 < size2) {
+ throw new EvioException("added data overflowed containing structure");
+ }
longData = new long[size1 + size2];
ByteDataTransformer.toLongArray(rawBytes, byteOrder, longData, 0);
System.arraycopy(data, 0, longData, size1, size2);
@@ -1920,6 +2245,10 @@
else {
int size1 = longData.length;
int size2 = data.length;
+
+ if (Integer.MAX_VALUE - size1 < size2) {
+ throw new EvioException("added data overflowed containing structure");
+ }
longData = Arrays.copyOf(longData, size1 + size2);
System.arraycopy(data, 0, longData, size1, size2);
numberDataItems += size2;
@@ -1935,7 +2264,8 @@
* Appends byte data to the structure. If the structure has no data, then this
* is the same as setting the data.
* @param data the byte data to append, or set if there is no existing data.
- * @throws EvioException
+ * @throws EvioException if adding data to a structure of a different data type;
+ * if data has too many elements to store in raw byte array (JVM limit)
*/
public void appendByteData(byte data[]) throws EvioException {
@@ -1956,6 +2286,10 @@
else {
int size1 = rawBytes.length - header.getPadding();
int size2 = data.length;
+
+ if (Integer.MAX_VALUE - size1 < size2) {
+ throw new EvioException("added data overflowed containing structure");
+ }
charData = new byte[size1 + size2];
System.arraycopy(rawBytes, 0, charData, 0, size1);
System.arraycopy(data, 0, charData, size1, size2);
@@ -1965,6 +2299,10 @@
else {
int size1 = charData.length;
int size2 = data.length;
+
+ if (Integer.MAX_VALUE - size1 < size2) {
+ throw new EvioException("added data overflowed containing structure");
+ }
charData = Arrays.copyOf(charData, size1 + size2);
System.arraycopy(data, 0, charData, size1, size2);
numberDataItems += data.length;
@@ -1973,10 +2311,15 @@
// store necessary padding to 4 byte boundaries.
int padding = padCount[numberDataItems%4];
header.setPadding(padding);
+//System.out.println("# data items = " + numberDataItems + ", padding = " + padding);
+ // Array creation sets everything to zero. Only need to copy in data.
+ if (Integer.MAX_VALUE - numberDataItems < padding) {
+ throw new EvioException("added data overflowed containing structure");
+ }
rawBytes = new byte[numberDataItems + padding];
System.arraycopy(charData, 0, rawBytes, 0, numberDataItems);
- System.arraycopy(padValues, 0, rawBytes, 0, padding);
+// System.arraycopy(padValues, 0, rawBytes, numberDataItems, padding); // unnecessary
lengthsUpToDate(false);
setAllHeaderLengths();
@@ -1986,7 +2329,8 @@
* Appends float data to the structure. If the structure has no data, then this
* is the same as setting the data.
* @param data the float data to append, or set if there is no existing data.
- * @throws EvioException
+ * @throws EvioException if adding data to a structure of a different data type;
+ * if data has too many elements to store in raw byte array (JVM limit)
*/
public void appendFloatData(float data[]) throws EvioException {
@@ -2007,6 +2351,10 @@
else {
int size1 = rawBytes.length/4;
int size2 = data.length;
+
+ if (Integer.MAX_VALUE - size1 < size2) {
+ throw new EvioException("added data overflowed containing structure");
+ }
floatData = new float[size1 + size2];
ByteDataTransformer.toFloatArray(rawBytes, byteOrder, floatData, 0);
System.arraycopy(data, 0, floatData, size1, size2);
@@ -2016,6 +2364,10 @@
else {
int size1 = floatData.length;
int size2 = data.length;
+
+ if (Integer.MAX_VALUE - size1 < size2) {
+ throw new EvioException("added data overflowed containing structure");
+ }
floatData = Arrays.copyOf(floatData, size1 + size2);
System.arraycopy(data, 0, floatData, size1, size2);
numberDataItems += data.length;
@@ -2029,9 +2381,10 @@
/**
* Appends string to the structure (as ascii). If the structure has no data, then this
- * is the same as setting the data.
+ * is the same as setting the data. Don't worry about checking for size limits since
+ * jevio structures will never contain a char array > {@link Integer#MAX_VALUE} in size.
* @param s the string to append (as ascii), or set if there is no existing data.
- * @throws EvioException
+ * @throws EvioException if adding data to a structure of a different data type
*/
public void appendStringData(String s) throws EvioException {
@@ -2114,7 +2467,8 @@
* Appends double data to the structure. If the structure has no data, then this
* is the same as setting the data.
* @param data the double data to append, or set if there is no existing data.
- * @throws EvioException
+ * @throws EvioException if adding data to a structure of a different data type;
+ * if data has too many elements to store in raw byte array (JVM limit)
*/
public void appendDoubleData(double data[]) throws EvioException {
@@ -2135,6 +2489,10 @@
else {
int size1 = rawBytes.length/8;
int size2 = data.length;
+
+ if (Integer.MAX_VALUE - size1 < size2) {
+ throw new EvioException("added data overflowed containing structure");
+ }
doubleData = new double[size1 + size2];
ByteDataTransformer.toDoubleArray(rawBytes, byteOrder, doubleData, 0);
System.arraycopy(data, 0, doubleData, size1, size2);
@@ -2144,6 +2502,10 @@
else {
int size1 = doubleData.length;
int size2 = data.length;
+
+ if (Integer.MAX_VALUE - size1 < size2) {
+ throw new EvioException("added data overflowed containing structure");
+ }
doubleData = Arrays.copyOf(doubleData, size1 + size2);
System.arraycopy(data, 0, doubleData, size1, size2);
numberDataItems += data.length;
@@ -2155,32 +2517,256 @@
setAllHeaderLengths();
}
+
/**
- * Sets the CompositeData object of this structure. No "append" mode for
- * this type of data exists.
- *
- * @param data CompositeData object
- * @throws EvioException
+ * Appends CompositeData objects to the structure. If the structure has no data, then this
+ * is the same as setting the data.
+ * @param data the CompositeData objects to append, or set if there is no existing data.
+ * @throws EvioException if adding data to a structure of a different data type;
+ * if data takes up too much memory to store in raw byte array (JVM limit)
*/
- public void appendCompositeData(CompositeData data) throws EvioException {
+ public void appendCompositeData(CompositeData data[]) throws EvioException {
DataType dataType = header.getDataType();
[truncated at 1000 lines; 247 more skipped]
jevio-base/src/main/java/org/jlab/coda/jevio/test
diff -u -r1.2 -r1.3
--- Tester.java 20 Mar 2012 23:21:49 -0000 1.2
+++ Tester.java 3 Apr 2013 20:07:47 -0000 1.3
@@ -2,6 +2,7 @@
import org.jlab.coda.jevio.*;
+import javax.swing.tree.DefaultTreeModel;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
@@ -39,7 +40,41 @@
System.out.println();
}
+ // Test to see when code chokes adding data to a bank
public static void main(String args[]) {
+ try {
+ EvioEvent bank = new EvioEvent(1, DataType.BANK, 1);
+ EvioBank ibank = new EvioBank (2, DataType.INT32, 2);
+ EvioBank bbank = new EvioBank (3, DataType.INT32, 3);
+ EvioBank lbank = new EvioBank (4, DataType.LONG64, 4);
+ //int[] data = new int[2500000];
+ // int[] data3 = new int[2147483646];
+ int[] data4 = new int[1];
+ int[] data2 = new int[1];
+ long[] data5 = new long[268435456];
+ // Keep adding to event
+ EventBuilder builder = new EventBuilder(bank);
+ builder.appendLongData(lbank, data5);
+ //builder.appendIntData(bbank, data3);
+ //builder.appendIntData(bbank, data4);
+ //ibank.appendIntData();
+ //ibank.setIntData(data2);
+ for (int i=0; i < 2000; i++) {
+ System.out.print(i + " ");
+ builder.addChild(bank, ibank);
+ }
+ // builder.appendIntData(ibank, data);
+ //ibank.appendIntData(data);
+
+ }
+ catch (EvioException e) {
+ e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
+ }
+
+
+ }
+
+ public static void main55(String args[]) {
byte[] data = new byte[] {(byte)1, (byte)2};
int off=0;
// treat the high bit improperly (shift first, then mask, high byte disappears)
@@ -51,6 +86,78 @@
System.out.println("littleE s1 = 0x"+ i1 + ", s2 = 0x" + i2);
}
+ public static void main33(String args[]) {
+ EvioEvent ev = new EvioEvent(1, DataType.INT32, 1);
+ EvioBank bank = new EvioBank (2, DataType.BANK, 2);
+ EvioBank ibank = new EvioBank (3, DataType.INT32, 3);
+ int[] data = new int[] {1,2,3,4,5};
+
+ // Build a bigger event tree
+// EventBuilder builder = new EventBuilder(ev);
+// try {
+// builder.addChild(ev, bank);
+// builder.addChild(bank, ibank);
+// }
+// catch (EvioException e) {}
+// ibank.setIntData(data);
+
+ try {
+ ev.setDictionaryXML("blah blah blah");
+ ev.appendIntData(data);
+ }
+ catch (EvioException e) {}
+ System.out.println("Now create the clone ...");
+
+ EvioEvent evClone = (EvioEvent)ev.clone();
+// EvioBank bkClone = (EvioBank)bank.clone();
+
+// DefaultTreeModel treeModel = ev.getTreeModel();
+// System.out.println("Tree model object for original event:\n" + treeModel);
+// System.out.println("Tree for original event:\n" + treeModel.toString());
+
+ int[] data1 = evClone.getIntData();
+// int[] data2 = bkClone.getIntData();
+
+ if (data1 != null) {
+ for (int i : data1) {
+ System.out.println("event i = " + i);
+ }
+ }
+ else {
+ System.out.println("event int data is NULL !!!");
+ }
+
+ String dict = evClone.getDictionaryXML();
+ if (dict != null) {
+ System.out.println("dictionary = \n" + dict);
+ }
+ else {
+ System.out.println("event dictionary is NULL !!!");
+ }
+
+
+ //int len = ((BankHeader)evClone.getHeader()).getHeaderLength();
+ //int len = evClone.getHeader().getHeaderLength();
+ int len = ev.getHeader().getHeaderLength();
+ System.out.println("header length = " + len);
+
+ System.out.println("Change ev tag from 1 to 66");
+ ev.getHeader().setTag(66);
+ System.out.println("\nev header = " + ev.getHeader().toString());
+ System.out.println("\nclone header = " + evClone.getHeader().toString());
+
+
+
+// if (data2 != null) {
+// for (int i : data2) {
+// System.out.println("bank i = " + i);
+// }
+// }
+// else {
+// System.out.println("bank int data is NULL !!!");
+// }
+ }
+
/** For testing only */
public static void main2(String args[]) {
@@ -67,8 +174,15 @@
doubleData[i] = i;
}
- byte[] rawBytesBE = ByteDataTransformer.toBytes(doubleData, ByteOrder.BIG_ENDIAN);
- byte[] rawBytesLE = ByteDataTransformer.toBytes(doubleData, ByteOrder.LITTLE_ENDIAN);
+ byte[] rawBytesBE = new byte[0];
+ byte[] rawBytesLE = new byte[0];
+ try {
+ rawBytesBE = ByteDataTransformer.toBytes(doubleData, ByteOrder.BIG_ENDIAN);
+ rawBytesLE = ByteDataTransformer.toBytes(doubleData, ByteOrder.LITTLE_ENDIAN);
+ }
+ catch (EvioException e) {
+ e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
+ }
byte[] rawBytes = rawBytesBE;
ByteBuffer byteBufferBE = ByteBuffer.allocate(1024);
@@ -173,17 +287,23 @@
//printDoubleBuffer(byteBuffer);
// option 7
- t1 = System.currentTimeMillis();
- for (int j=0; j < LOOPS; j++) {
- byteBuffer.clear();
- byte[] bytes = ByteDataTransformer.toBytes(doubleData, byteBuffer.order());
- byteBuffer.put(bytes);
+
+ try {
+ t1 = System.currentTimeMillis();
+ for (int j=0; j < LOOPS; j++) {
+ byteBuffer.clear();
+ byte[] bytes = ByteDataTransformer.toBytes(doubleData, byteBuffer.order());
+ byteBuffer.put(bytes);
+ }
+ t2 = System.currentTimeMillis();
+ deltaT = t2 - t1;
+ freq = (double) LOOPS / deltaT * 1000;
+ System.out.printf("loopRate D7: %2.3g Hz, time = %2.3g sec\n", freq, (deltaT/1000.));
+ //printDoubleBuffer(byteBuffer);
+ }
+ catch (EvioException e) {
+ e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
- t2 = System.currentTimeMillis();
- deltaT = t2 - t1;
- freq = (double) LOOPS / deltaT * 1000;
- System.out.printf("loopRate D7: %2.3g Hz, time = %2.3g sec\n", freq, (deltaT/1000.));
- //printDoubleBuffer(byteBuffer);
// option 8
@@ -235,8 +355,15 @@
intData[i] = i;
}
- byte[] rawBytesIBE = ByteDataTransformer.toBytes(intData, ByteOrder.BIG_ENDIAN);
- byte[] rawBytesILE = ByteDataTransformer.toBytes(intData, ByteOrder.LITTLE_ENDIAN);
+ byte[] rawBytesIBE = new byte[0];
+ byte[] rawBytesILE = new byte[0];
+ try {
+ rawBytesIBE = ByteDataTransformer.toBytes(intData, ByteOrder.BIG_ENDIAN);
+ rawBytesILE = ByteDataTransformer.toBytes(intData, ByteOrder.LITTLE_ENDIAN);
+ }
+ catch (EvioException e) {
+ e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
+ }
byte[] rawBytesI = rawBytesIBE;
ByteBuffer byteBufferIBE = ByteBuffer.allocate(1024);
@@ -341,17 +468,23 @@
//printIntBuffer(byteBufferI);
// option 7
- t1 = System.currentTimeMillis();
- for (int j=0; j < LOOPS; j++) {
- byteBufferI.clear();
- byte[] bytes = ByteDataTransformer.toBytes(intData, byteBufferI.order());
- byteBufferI.put(bytes);
+
+ try {
+ t1 = System.currentTimeMillis();
+ for (int j=0; j < LOOPS; j++) {
+ byteBufferI.clear();
+ byte[] bytes = ByteDataTransformer.toBytes(intData, byteBufferI.order());
+ byteBufferI.put(bytes);
+ }
+ t2 = System.currentTimeMillis();
+ deltaT = t2 - t1;
+ freq = (double) LOOPS / deltaT * 1000;
+ System.out.printf("loopRate I7: %2.3g Hz, time = %2.3g sec\n", freq, (deltaT/1000.));
+ //printIntBuffer(byteBufferI);
+ }
+ catch (EvioException e) {
+ e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
- t2 = System.currentTimeMillis();
- deltaT = t2 - t1;
- freq = (double) LOOPS / deltaT * 1000;
- System.out.printf("loopRate I7: %2.3g Hz, time = %2.3g sec\n", freq, (deltaT/1000.));
- //printIntBuffer(byteBufferI);
// option 8 (really slow)
// t1 = System.currentTimeMillis();
@@ -612,8 +745,15 @@
doubleData[i] = i;
}
- byte[] rawBytesIBE = ByteDataTransformer.toBytes(intData, ByteOrder.BIG_ENDIAN);
- byte[] rawBytesILE = ByteDataTransformer.toBytes(intData, ByteOrder.LITTLE_ENDIAN);
+ byte[] rawBytesIBE = new byte[0];
+ byte[] rawBytesILE = new byte[0];
+ try {
+ rawBytesIBE = ByteDataTransformer.toBytes(intData, ByteOrder.BIG_ENDIAN);
+ rawBytesILE = ByteDataTransformer.toBytes(intData, ByteOrder.LITTLE_ENDIAN);
+ }
+ catch (EvioException e) {
+ e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
+ }
byte[] rawBytesI = rawBytesILE;
jevio-base/src/main/java/org/jlab/coda/jevio/test
diff -u -r1.1 -r1.2
--- FileTest.java 28 Feb 2012 19:41:37 -0000 1.1
+++ FileTest.java 3 Apr 2013 20:07:47 -0000 1.2
@@ -2,10 +2,11 @@
import org.jlab.coda.jevio.*;
-import java.io.File;
-import java.io.IOException;
+import java.io.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
+import java.nio.channels.FileChannel;
+import java.util.Arrays;
import java.util.List;
/**
@@ -15,217 +16,200 @@
*/
public class FileTest {
- /** For testing only */
- public static void main0(String args[]) {
- //create an event writer to write out the test events.
- String fileName = "../../testdata/BigOut3.ev";
- File file = new File(fileName);
-
- ByteBuffer myBuf = ByteBuffer.allocate(2000);
- myBuf.order(ByteOrder.LITTLE_ENDIAN);
-
- // xml dictionary
- String dictionary =
- "<xmlDict>\n" +
- " <xmldumpDictEntry name=\"bank of segs\" tag=\"1\" num=\"1\"/>\n" +
- " <xmldumpDictEntry name=\"seg of banks\" tag=\"2\" />\n" +
- " <xmldumpDictEntry name=\"shorts pad0\" tag=\"3\" num=\"1.3\"/>\n" +
- " <xmldumpDictEntry name=\"shorts pad2\" tag=\"4\" num=\"1.4\"/>\n" +
- " <xmldumpDictEntry name=\"bank of chars\" tag=\"5\" num=\"5\"/>\n" +
- " <xmldumpDictEntry name=\"chars pad0\" tag=\"6\" num=\"5.6\"/>\n" +
- " <xmldumpDictEntry name=\"chars pad3\" tag=\"7\" num=\"5.7\"/>\n" +
- " <xmldumpDictEntry name=\"chars pad2\" tag=\"8\" num=\"5.8\"/>\n" +
- " <xmldumpDictEntry name=\"chars pad1\" tag=\"9\" num=\"5.9\"/>\n" +
- "</xmlDict>";
-
- // data
- byte[] byteData1 = new byte[] {1,2,3,4};
- byte[] byteData2 = new byte[] {1,2,3,4,5};
- byte[] byteData3 = new byte[] {1,2,3,4,5,6};
- byte[] byteData4 = new byte[] {1,2,3,4,5,6,7};
- short[] shortData1 = new short[] {11,22};
- short[] shortData2 = new short[] {11,22,33};
- try {
- EventWriter eventWriterNew = new EventWriter(file, 100, 3,
- ByteOrder.BIG_ENDIAN, dictionary, null);
-
- // event - bank of banks
- EventBuilder eventBuilder2 = new EventBuilder(1, DataType.SEGMENT, 1);
- EvioEvent eventShort = eventBuilder2.getEvent();
-
- // bank of short banks
- EvioSegment segBanks = new EvioSegment(2, DataType.BANK);
-
- // 3 shorts
- EvioBank shortBank1 = new EvioBank(3, DataType.SHORT16, 3);
- shortBank1.appendShortData(shortData1);
- eventBuilder2.addChild(segBanks, shortBank1);
-
- EvioBank shortBank2 = new EvioBank(4, DataType.SHORT16, 4);
- shortBank2.appendShortData(shortData2);
- eventBuilder2.addChild(segBanks, shortBank2);
-
- eventBuilder2.addChild(eventShort, segBanks);
- eventWriterNew.writeEvent(eventShort);
-
-
-
- // each event is a trivial event containing an array of ints - all zeros
- EventBuilder eventBuilder = new EventBuilder(5, DataType.BANK, 5);
- EvioEvent event = eventBuilder.getEvent();
-
- // event 1
- EvioBank charBank1 = new EvioBank(6, DataType.CHAR8, 6);
- charBank1.appendByteData(byteData1);
- eventBuilder.addChild(event, charBank1);
-
- // event 2
- EvioBank charBank2 = new EvioBank(7, DataType.CHAR8, 7);
- charBank2.appendByteData(byteData2);
- eventBuilder.addChild(event, charBank2);
-
- // event 3
- EvioBank charBank3 = new EvioBank(8, DataType.CHAR8, 8);
- charBank3.appendByteData(byteData3);
- eventBuilder.addChild(event, charBank3);
-
- // event 4
- EvioBank charBank4 = new EvioBank(9, DataType.CHAR8, 9);
- charBank4.appendByteData(byteData4);
- eventBuilder.addChild(event, charBank4);
+ /** For testing the speed difference in writeEvent algorithms.
+ * Want about 1000 little events/block. */
+ public static void main11(String args[]) {
- eventWriterNew.writeEvent(event);
+ // Create an event writer to write out the test events.
+ String fileName = "/daqfs/home/timmer/coda/jevio-4.0/testdata/speedTest.ev";
+ File file = new File(fileName);
- // all done writing
- eventWriterNew.close();
- }
- catch (IOException e) {
- e.printStackTrace();
- }
- catch (EvioException e) {
- e.printStackTrace();
- }
+ // data
+ byte[] byteData1 = new byte[499990];
+ int num, count = 0;
+ long t1=0, t2=0, time, totalT=0, totalCount=0;
+ double rate, avgRate;
- File fileIn = new File(fileName);
- System.out.println("read ev file: " + fileName + " size: " + fileIn.length());
+ try {
+ // 1MB max block size, 2 max # events/block
+ EventWriter eventWriter = new EventWriter(file, 1000000, 2,
+ ByteOrder.BIG_ENDIAN, null, null);
+
+ // event -> bank of bytes
+ // each event (including header) is 100 bytes
+ EventBuilder eventBuilder = new EventBuilder(1, DataType.CHAR8, 1);
+ EvioEvent ev = eventBuilder.getEvent();
+ ev.appendByteData(byteData1);
+
+ // keep track of time
+ t1 = System.currentTimeMillis();
+
+
+ for (int j=0; j < 10; j++) {
+// 10 MB file with 10 block headers
+ for (int i=0; i < 2; i++) {
+ eventWriter.writeEvent(ev);
+ count++;
+ }
+ }
- try {
- EvioReader evioReader = new EvioReader(fileName);
- EvioEvent ev = evioReader.parseNextEvent();
- List<BaseStructure> l = StructureFinder.getMatchingStructures(ev,"seg of banks",
- NameProviderFactory.createNameProvider(dictionary));
+ // all done writing
+ eventWriter.close();
+ // calculate the event rate
+ t2 = System.currentTimeMillis();
+ time = t2 - t1;
+ rate = 1000.0 * ((double) count) / time;
+ totalCount += count;
+ totalT += time;
+ avgRate = 1000.0 * ((double) totalCount) / totalT;
+ System.out.println("rate = " + String.format("%.3g", rate) +
+ " Hz, avg = " + String.format("%.3g", avgRate));
+ System.out.println("time = " + (time) + " milliseconds");
+ count = 0;
+ t1 = System.currentTimeMillis();
+ }
+ catch (IOException e) {
+ e.printStackTrace();
+ }
+ catch (EvioException e) {
+ e.printStackTrace();
+ }
- if (l != null) {
- for (BaseStructure str : l) {
- System.out.println("Found bank: tag = " + str.getHeader().getTag() +
- ", num = " + str.getHeader().getNumber());
- }
- }
- else {
- System.out.println("Found NO banks dude");
- }
- }
- catch (EvioException e) {
- e.printStackTrace();
- }
- catch (IOException e) {
- e.printStackTrace();
- }
- }
+ }
- /** For testing only */
- public static void main7(String args[]) {
+ /** For WRITING a local file. */
+ public static void main12(String args[]) {
- //create an event writer to write out the test events.
- String fileName = "../../testdata/BigOut3.ev";
+ // String fileName = "./myData.ev";
+ String fileName = "/daqfs/home/timmer/coda/jevio-4.0/testdata/fileTest.ev";
File file = new File(fileName);
-
- ByteBuffer myBuf = ByteBuffer.allocate(10000);
- myBuf.order(ByteOrder.LITTLE_ENDIAN);
+ ByteBuffer myBuf = null;
// xml dictionary
- String dictionary =
- "<xmlDict>\n" +
- " <xmldumpDictEntry name=\"bank\" tag=\"1\" num=\"1\"/>\n" +
- " <xmldumpDictEntry name=\"bank of shorts\" tag=\"2\" num=\"1.2\"/>\n" +
- " <xmldumpDictEntry name=\"shorts pad0\" tag=\"3\" num=\"1.2.3\"/>\n" +
- " <xmldumpDictEntry name=\"shorts pad2\" tag=\"4\" num=\"1.2.4\"/>\n" +
- " <xmldumpDictEntry name=\"bank of chars\" tag=\"5\" num=\"5\"/>\n" +
- " <xmldumpDictEntry name=\"chars pad0\" tag=\"6\" num=\"5.6\"/>\n" +
- " <xmldumpDictEntry name=\"chars pad3\" tag=\"7\" num=\"5.7\"/>\n" +
- " <xmldumpDictEntry name=\"chars pad2\" tag=\"8\" num=\"5.8\"/>\n" +
- " <xmldumpDictEntry name=\"chars pad1\" tag=\"9\" num=\"5.9\"/>\n" +
- "</xmlDict>";
+ String xmlDictionary =
+ "<xmlDict>\n" +
+ " <bank name=\"bank of banks\" tag=\"1\" num=\"1\">\n" +
+ " <bank name=\"bank of segments\" tag=\"2\" num=\"2\">\n" +
+ " <leaf name=\"segment of shorts\" tag=\"3\" />\n" +
+ " </bank>\n" +
+ " <bank name=\"bank of banks\" tag=\"4\" num=\"4\">\n" +
+ " <leaf name=\"bank of chars\" tag=\"5\" num=\"5\"/>\n" +
+ " </bank>\n" +
+ " </bank>\n" +
+ " <dictEntry name=\"last bank\" tag=\"33\" num=\"66\"/>\n" +
+ "</xmlDict>";
// data
- byte[] byteData1 = new byte[] {1,2,3,4};
- byte[] byteData2 = new byte[] {1,2,3,4,5};
- byte[] byteData3 = new byte[] {1,2,3,4,5,6};
- byte[] byteData4 = new byte[] {1,2,3,4,5,6,7};
- short[] shortData1 = new short[] {11,22};
- short[] shortData2 = new short[] {11,22,33};
-
- try {
- EventWriter eventWriterNew = new EventWriter(file, 100, 3,
- ByteOrder.BIG_ENDIAN, dictionary, null);
-
- // event - bank of banks
- EventBuilder eventBuilder2 = new EventBuilder(1, DataType.BANK, 1);
- EvioEvent eventShort = eventBuilder2.getEvent();
+ byte[] byteData1 = new byte[] {1,2,3,4,5};
+ int[] intData1 = new int[] {4,5,6};
+ int[] intData2 = new int[] {7,8,9};
+ short[] shortData = new short[] {11,22,33};
- // bank of short banks
- EvioBank bankBanks = new EvioBank(2, DataType.BANK, 2);
-
- // 3 shorts
- EvioBank shortBank1 = new EvioBank(3, DataType.SHORT16, 3);
- shortBank1.appendShortData(shortData1);
- eventBuilder2.addChild(bankBanks, shortBank1);
-
- EvioBank shortBank2 = new EvioBank(3, DataType.SHORT16, 3);
- shortBank2.appendShortData(shortData2);
- eventBuilder2.addChild(bankBanks, shortBank2);
-
- eventBuilder2.addChild(eventShort, bankBanks);
- eventWriterNew.writeEvent(eventShort);
+ // Do we overwrite or append?
+ boolean append = false;
+ // Do we write to file or buffer?
+ boolean useFile = true;
+ // Top level event
+ EvioEvent event = null;
- // each event is a trivial event containing an array of ints - all zeros
- EventBuilder eventBuilder = new EventBuilder(5, DataType.BANK, 5);
- EvioEvent event = eventBuilder.getEvent();
-
- // event 1
- EvioBank charBank1 = new EvioBank(6, DataType.CHAR8, 6);
- charBank1.appendByteData(byteData1);
- eventBuilder.addChild(event, charBank1);
-
- // event 2
- EvioBank charBank2 = new EvioBank(7, DataType.CHAR8, 7);
- charBank2.appendByteData(byteData2);
- eventBuilder.addChild(event, charBank2);
-
- // event 3
- EvioBank charBank3 = new EvioBank(8, DataType.CHAR8, 8);
- charBank3.appendByteData(byteData3);
- eventBuilder.addChild(event, charBank3);
-
- // event 4
- EvioBank charBank4 = new EvioBank(9, DataType.CHAR8, 9);
- charBank4.appendByteData(byteData4);
- eventBuilder.addChild(event, charBank4);
+ try {
+ // Create an event writer to write out the test events to file
+ EventWriter writer;
- eventWriterNew.writeEvent(event);
+ if (useFile) {
+ writer = new EventWriter(file, xmlDictionary, append);
+ }
+ else {
+ // Create an event writer to write to buffer
+ myBuf = ByteBuffer.allocate(10000);
+ myBuf.order(ByteOrder.LITTLE_ENDIAN);
+ writer = new EventWriter(myBuf, xmlDictionary, append);
+ }
- // all done writing
- eventWriterNew.close();
+// // Build event (bank of banks) without an EventBuilder
+// event = new EvioEvent(1, DataType.BANK, 1);
+//
+// // bank of segments
+// EvioBank bankSegs = new EvioBank(2, DataType.SEGMENT, 2);
+// event.insert(bankSegs);
+//
+// // segment of 3 shorts
+// EvioSegment segShorts = new EvioSegment(3, DataType.SHORT16);
+// segShorts.setShortData(shortData);
+// bankSegs.insert(segShorts);
+// bankSegs.remove(segShorts);
+//
+// // another bank of banks
+// EvioBank bankBanks = new EvioBank(4, DataType.BANK, 4);
+// event.insert(bankBanks);
+//
+// // bank of chars
+// EvioBank charBank = new EvioBank(5, DataType.CHAR8, 5);
+// charBank.setByteData(byteData1);
+// bankBanks.insert(charBank);
+//
+// event.setAllHeaderLengths();
+
+ // Build event (bank of banks) with EventBuilder object
+ EventBuilder builder = new EventBuilder(1, DataType.BANK, 1);
+ event = builder.getEvent();
+
+ // bank of segments
+ EvioBank bankSegs = new EvioBank(2, DataType.SEGMENT, 2);
+ builder.addChild(event, bankSegs);
+
+ // segment of 3 shorts
+ EvioSegment segShorts = new EvioSegment(3, DataType.SHORT16);
+ segShorts.appendShortData(shortData);
+ builder.addChild(bankSegs, segShorts);
+ //builder.remove(segShorts);
+
+ // another bank of banks
+ EvioBank bankBanks = new EvioBank(4, DataType.BANK, 4);
+ builder.addChild(event, bankBanks);
+
+ // bank of chars
+ EvioBank charBank = new EvioBank(5, DataType.CHAR8, 5);
+ charBank.appendByteData(byteData1);
+ builder.addChild(bankBanks, charBank);
+ // Write event to file
+ writer.writeEvent(event);
+
+ // How much room do I have left in the buffer now?
+ if (!useFile) {
+ System.out.println("Buffer has " + myBuf.remaining() + " bytes left");
+ }
+ // event - bank of banks
+ EvioEvent lastEvent = new EvioEvent(33, DataType.INT32, 66);
+ // Call this BEFORE appending data!
+ lastEvent.setByteOrder(ByteOrder.LITTLE_ENDIAN);
+ lastEvent.appendIntData(intData1);
+ lastEvent.setIntData(intData2);
+ lastEvent.appendIntData(intData2);
+
+ // Write last event to file
+ writer.writeEvent(lastEvent);
+
+ // All done writing
+ writer.close();
+
+
+ // Transform segments into banks in 2 different ways
+ EvioBank segBank1 = StructureTransformer.transform(segShorts, 10);
+ StructureTransformer T = new StructureTransformer();
+ EvioBank segBank2 = T.transform(segShorts, 10);
+ EvioXMLDictionary dict = new EvioXMLDictionary(xmlDictionary);
+ NameProvider.setProvider(dict);
}
catch (IOException e) {
e.printStackTrace();
@@ -235,157 +219,127 @@
}
- File fileIn = new File(fileName);
- System.out.println("read ev file: " + fileName + " size: " + fileIn.length());
-
try {
- EvioReader evioReader = new EvioReader(fileName);
- EvioEvent ev = evioReader.parseNextEvent();
- List<BaseStructure> l = StructureFinder.getMatchingStructures(ev,"bank of shorts",
- NameProviderFactory.createNameProvider(dictionary));
-
- if (l != null) {
- for (BaseStructure str : l) {
- System.out.println("Found bank: tag = " + str.getHeader().getTag() +
- ", num = " + str.getHeader().getNumber());
- }
+ EvioReader evioReader;
+ if (useFile) {
+ System.out.println("read ev file: " + fileName + ", size: " + file.length());
+ evioReader = new EvioReader(fileName);
}
else {
- System.out.println("Found NO banks dude");
+ myBuf.flip();
+ evioReader = new EvioReader(myBuf);
}
+ EventParser parser = evioReader.getParser();
- }
- catch (EvioException e) {
- e.printStackTrace();
- }
- catch (IOException e) {
- e.printStackTrace();
- }
- }
-
-
- /** For testing only */
- public static void main8(String args[]) {
-
- //create an event writer to write out the test events.
- String fileName = "../../testdata/BigOut3.ev";
- File file = new File(fileName);
-
- ByteBuffer myBuf = ByteBuffer.allocate(10000);
- myBuf.order(ByteOrder.LITTLE_ENDIAN);
+ IEvioListener listener = new IEvioListener() {
+ @Override
+ public void gotStructure(BaseStructure topStructure, IEvioStructure structure) {
+ System.out.println("Parsed structure of type " + structure.getStructureType());
+ }
- // xml dictionary
- String dictionary =
- "<xmlDict>\n" +
- " <xmldumpDictEntry name=\"bank\" tag=\"1\" num=\"1\"/>\n" +
- " <xmldumpDictEntry name=\"bank of shorts\" tag=\"2\" num=\"2\"/>\n" +
- " <xmldumpDictEntry name=\"shorts pad0\" tag=\"3\" num=\"3\"/>\n" +
- " <xmldumpDictEntry name=\"shorts pad2\" tag=\"4\" num=\"4\"/>\n" +
- " <xmldumpDictEntry name=\"bank of chars\" tag=\"5\" num=\"5\"/>\n" +
- " <xmldumpDictEntry name=\"chars pad0\" tag=\"6\" num=\"6\"/>\n" +
- " <xmldumpDictEntry name=\"chars pad3\" tag=\"7\" num=\"7\"/>\n" +
- " <xmldumpDictEntry name=\"chars pad2\" tag=\"8\" num=\"8\"/>\n" +
- " <xmldumpDictEntry name=\"chars pad1\" tag=\"9\" num=\"9\"/>\n" +
- "</xmlDict>";
+ @Override
+ public void startEventParse(BaseStructure structure) {
+ System.out.println("Starting event parse");
+ }
- // data
- byte[] byteData1 = new byte[] {1,2,3,4};
- byte[] byteData2 = new byte[] {1,2,3,4,5};
- byte[] byteData3 = new byte[] {1,2,3,4,5,6};
- byte[] byteData4 = new byte[] {1,2,3,4,5,6,7};
- short[] shortData1 = new short[] {11,22};
- short[] shortData2 = new short[] {11,22,33};
+ @Override
+ public void endEventParse(BaseStructure structure) {
+ System.out.println("Ended event parse");
+ }
+ };
- try {
- EventWriter eventWriterNew = new EventWriter(file, 100, 3,
- ByteOrder.BIG_ENDIAN, dictionary, null);
+ parser.addEvioListener(listener);
- // event - bank of banks
- EventBuilder eventBuilder2 = new EventBuilder(1, DataType.BANK, 1);
- EvioEvent eventShort = eventBuilder2.getEvent();
+ // Get any existing dictionary (should be the same as "xmlDictionary")
+ String xmlDictString = evioReader.getDictionaryXML();
+ EvioXMLDictionary dictionary = null;
- // bank of short banks
- EvioBank bankBanks = new EvioBank(2, DataType.BANK, 2);
+ if (xmlDictString == null) {
+ System.out.println("Ain't got no dictionary!");
+ }
+ else {
+ // Create dictionary object from xml string
+ dictionary = new EvioXMLDictionary(xmlDictString);
+ System.out.println("Got a dictionary:\n" + dictionary.toString());
+ }
- // 3 shorts
- EvioBank shortBank1 = new EvioBank(3, DataType.SHORT16, 3);
- shortBank1.appendShortData(shortData1);
- eventBuilder2.addChild(bankBanks, shortBank1);
+ // How many events in the file?
+ int evCount = evioReader.getEventCount();
+ System.out.println("Read file, got " + evCount + " events:\n");
+
+ // Use the "random access" capability to look at last event (starts at 1)
+ EvioEvent ev = evioReader.parseEvent(evCount);
+ System.out.println("Last event = " + ev.toString());
+
+ // Print out any data in the last event.
+ //
+ // In the writing example, the data for this event was set to
+ // be little endian so we need to read it in that way too
+ ev.setByteOrder(ByteOrder.LITTLE_ENDIAN);
+ int[] intData = ev.getIntData();
+ if (intData != null) {
+ for (int i=0; i < intData.length; i++) {
+ System.out.println("intData[" + i + "] = " + intData[i]);
+ }
+ }
- EvioBank shortBank2 = new EvioBank(4, DataType.SHORT16, 4);
- shortBank2.appendShortData(shortData2);
- eventBuilder2.addChild(bankBanks, shortBank2);
+ // Use the dictionary
+ if (dictionary != null) {
+ String eventName = dictionary.getName(ev);
+ System.out.println("Name of last event = " + eventName);
+ }
- eventBuilder2.addChild(eventShort, bankBanks);
- eventWriterNew.writeEvent(eventShort);
+ // Use sequential access to events
+ while ( (ev = evioReader.parseNextEvent()) != null) {
+ System.out.println("Event = " + ev.toString());
+ }
+ // Go back to the beginning of file/buffer
+ evioReader.rewind();
+ // Search for banks/segs/tagsegs with a particular tag & num pair of values
+ int tag=1, num=1;
+ List<BaseStructure> list = StructureFinder.getMatchingBanks(
+ (ev = evioReader.parseNextEvent()), tag, num);
+ System.out.println("Event = " + ev.toString());
+ for (BaseStructure s : list) {
+ System.out.println("Evio structure named \"" + dictionary.getName(s) +
+ "\" has tag=1 & num=1");
+ }
- // each event is a trivial event containing an array of ints - all zeros
- EventBuilder eventBuilder = new EventBuilder(5, DataType.BANK, 5);
- EvioEvent event = eventBuilder.getEvent();
-
- // event 1
- EvioBank charBank1 = new EvioBank(6, DataType.CHAR8, 6);
- charBank1.appendByteData(byteData1);
- eventBuilder.addChild(event, charBank1);
-
- // event 2
- EvioBank charBank2 = new EvioBank(7, DataType.CHAR8, 7);
- charBank2.appendByteData(byteData2);
- eventBuilder.addChild(event, charBank2);
-
- // event 3
- EvioBank charBank3 = new EvioBank(8, DataType.CHAR8, 8);
- charBank3.appendByteData(byteData3);
- eventBuilder.addChild(event, charBank3);
-
- // event 4
- EvioBank charBank4 = new EvioBank(9, DataType.CHAR8, 9);
- charBank4.appendByteData(byteData4);
- eventBuilder.addChild(event, charBank4);
+ // ------------------------------------------------------------------
+ // Search for banks/segs/tagsegs with a custom set of search criteria
+ // ------------------------------------------------------------------
+
+ // This filter selects Segment structures that have odd numbered tags.
+ class myEvioFilter implements IEvioFilter {
+ public boolean accept(StructureType structureType, IEvioStructure struct) {
+ return (structureType == StructureType.SEGMENT &&
+ (struct.getHeader().getTag() % 2 == 1));
+ }
+ };
- eventWriterNew.writeEvent(event);
+ myEvioFilter filter = new myEvioFilter();
+ list = StructureFinder.getMatchingStructures(event, filter);
+ if (list != null) {
+ System.out.println("list size = " + list.size());
+ for (BaseStructure s : list) {
+ System.out.println("Evio structure named " + dictionary.getName(s) +
+ " is a segment with an odd numbered tag");
+ }
+ }
- // all done writing
- eventWriterNew.close();
- }
- catch (IOException e) {
- e.printStackTrace();
+ // ------------------------------------------------------------------
}
catch (EvioException e) {
e.printStackTrace();
}
-
-
- File fileIn = new File(fileName);
- System.out.println("read ev file: " + fileName + " size: " + fileIn.length());
-
- try {
- EvioReader evioReader = new EvioReader(fileName);
- System.out.println("EvioFile exe: now do output to xml file");
- evioReader.toXMLFile("/daqfs/home/timmer/coda/jevio-4.0/testdata/BigOut3.xml");
-
-// ByteBuffer mBuf = evioReader.getMappedByteBuffer();
-
-// System.out.println("i File myBuffer");
-// for (int i=0; i < mBuf.limit(); i+=4) {
-// System.out.println((i/4)+ " " + mBuf.getInt(i) + " " + myBuf.getInt(i));
-// }
-// for (int i=0; i < mBuf.limit(); i+=4) {
-// if (mBuf.getInt(i) != myBuf.getInt(i)) {
-// System.out.print((i/4)+" ");
-// }
-// }
-
- }
catch (IOException e) {
e.printStackTrace();
}
}
-
/** For testing only */
public static void main9(String args[]) {
@@ -394,7 +348,6 @@
File file = new File(fileName);
ByteBuffer myBuf = ByteBuffer.allocate(800);
- //myBuf.order(ByteOrder.LITTLE_ENDIAN);
// xml dictionary
String dictionary =
@@ -404,17 +357,9 @@
" <xmldumpDictEntry name=\"shorts pad0\" tag=\"3\" num=\"1.2.3\"/>\n" +
" <xmldumpDictEntry name=\"shorts pad2\" tag=\"4\" num=\"1.2.4\"/>\n" +
" <xmldumpDictEntry name=\"bank of chars\" tag=\"5\" num=\"5\"/>\n" +
- " <xmldumpDictEntry name=\"chars pad0\" tag=\"6\" num=\"5.6\"/>\n" +
- " <xmldumpDictEntry name=\"chars pad3\" tag=\"7\" num=\"5.7\"/>\n" +
- " <xmldumpDictEntry name=\"chars pad2\" tag=\"8\" num=\"5.8\"/>\n" +
- " <xmldumpDictEntry name=\"chars pad1\" tag=\"9\" num=\"5.9\"/>\n" +
"</xmlDict>";
// data
- byte[] byteData1 = new byte[] {1,2,3,4};
- byte[] byteData2 = new byte[] {1,2,3,4,5};
- byte[] byteData3 = new byte[] {1,2,3,4,5,6};
- byte[] byteData4 = new byte[] {1,2,3,4,5,6,7};
short[] shortData1 = new short[] {11,22};
short[] shortData2 = new short[] {11,22,33};
@@ -564,4 +509,267 @@
}
}
+
+ /**
+ 3 block headers (first 2 have 2 extra words each, last has 1 extra word).
+ First block has 2 events. Second has 3 events.
+ Last is empty final block.
+ */
+ static int data1[] = {
+ 0x00000014,
+ 0x00000001,
+ 0x0000000A,
+ 0x00000002,
+ 0x00000000,
+ 0x00000004,
+ 0x00000000,
+ 0xc0da0100,
+ 0x00000003,
+ 0x00000002,
+
+ 0x00000004,
+ 0x00010101,
+ 0x00000001,
+ 0x00000001,
+ 0x00000001,
+
+ 0x00000004,
+ 0x00010101,
+ 0x00000002,
+ 0x00000002,
+ 0x00000002,
+
+ 0x00000019,
+ 0x00000002,
+ 0x0000000A,
+ 0x00000003,
+ 0x00000000,
+ 0x00000004,
+ 0x00000000,
+ 0xc0da0100,
+ 0x00000001,
+ 0x00000002,
+
+ 0x00000004,
+ 0x00010101,
+ 0x00000003,
+ 0x00000003,
+ 0x00000003,
+
+ 0x00000004,
+ 0x00010101,
+ 0x00000004,
+ 0x00000004,
+ 0x00000004,
+
+ 0x00000004,
+ 0x00010101,
+ 0x00000005,
+ 0x00000005,
+ 0x00000005,
+
+ 0x00000009,
+ 0x00000003,
+ 0x00000009,
+ 0x00000000,
+ 0x00000000,
+ 0x00000204,
+ 0x00000000,
+ 0xc0da0100,
+ 0x00000003,
+ };
+
+
+ /** For writing out a 5GByte file. */
+ public static void main(String args[]) {
+
+ // write evio file that has extra words in headers
+ if (false) {
+ try {
+ byte[] be = ByteDataTransformer.toBytes(data1, ByteOrder.BIG_ENDIAN);
+ ByteBuffer buf = ByteBuffer.wrap(be);
+ String fileName = "/local/scratch/HeaderFile.ev";
+ File file = new File(fileName);
+ FileOutputStream fileOutputStream = new FileOutputStream(file);
+ FileChannel fileChannel = fileOutputStream.getChannel();
+ fileChannel.write(buf);
+ fileChannel.close();
+ }
+ catch (Exception e) {
+ e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
+ }
+
+ }
+
+ //create an event writer to write out the test events.
+ //String fileName = "/local/scratch/BigFile.ev";
+ String fileName = "/local/scratch/LittleFile.ev";
+ //String fileName = "/local/scratch/HeaderFile.ev";
+ File file = new File(fileName);
+ EvioEvent event;
+
+ if (false) {
+// data
+ int[] intData = new int[2500000];
+ Arrays.fill(intData, 23);
+
+
+ try {
+ EventWriter eventWriterNew = new EventWriter(file, 1000, 3, ByteOrder.BIG_ENDIAN, null, null);
+
+ // event - bank of banks
+ EventBuilder eventBuilder2 = new EventBuilder(1, DataType.INT32, 1);
+ event = eventBuilder2.getEvent();
+ event.appendIntData(intData);
+
+ // 300 ev/file * 10MB/ev = 3GB/file
+ for (int i=0; i < 300; i++) {
+ eventWriterNew.writeEvent(event);
+ System.out.print(".");
+ }
+ System.out.println("\nDONE");
+
+ // all done writing
+ eventWriterNew.close();
+ }
+ catch (IOException e) {
+ e.printStackTrace();
+ }
+ catch (EvioException e) {
+ e.printStackTrace();
+ }
+ }
+
+ if (false) {
+// data
+ int[] intData = new int[1];
+
+
+ try {
+ EventWriter eventWriterNew = new EventWriter(file, 1000, 3, ByteOrder.BIG_ENDIAN, null, null);
+
+ // event - bank of banks
+ EventBuilder eventBuilder2 = new EventBuilder(1, DataType.INT32, 1);
+ event = eventBuilder2.getEvent();
+ event.appendIntData(intData);
+
+ // 300 ev/file * 4bytes/ev = 1.2kB/file
+ for (int i=0; i < 300; i++) {
+ eventWriterNew.writeEvent(event);
+ System.out.print(".");
+ }
+ System.out.println("\nDONE");
+
+ // all done writing
+ eventWriterNew.close();
+ }
+ catch (IOException e) {
+ e.printStackTrace();
+ }
+ catch (EvioException e) {
+ e.printStackTrace();
+ }
+ }
+
+ if (true) {
+ System.out.println("\nTRY READING");
+
+ File fileIn = new File(fileName);
+ System.out.println("read ev file: " + fileName + " size: " + fileIn.length());
+ try {
+ EvioReader fileReader = new EvioReader(fileName);
+ int evCount = fileReader.getEventCount();
+ System.out.println("\nnum ev = " + evCount);
+ System.out.println("dictionary = " + fileReader.getDictionaryXML() + "\n");
+
+ long t2, t1 = System.currentTimeMillis();
+ // test sequential after random
+ event = fileReader.getEvent(290);
+ System.out.println("Got 290th event");
+
+ int counter = 0;
+ while ( (event = fileReader.parseNextEvent()) != null) {
+ if (event == null) {
+ System.out.println("We got a NULL event !!!");
+ return;
+ }
+ System.out.println("Event #" + counter++ + " =\n" + event);
+
+// if (counter++ %10 ==0) {
+// System.out.println("Event #" + counter + " =\n" + event);
+// int[] d = event.getIntData();
+// System.out.println("Data[0] = " + d[0] + ", Data[last] = " + d[d.length-1]);
+// // System.out.println("Event = \n" + event.toXML());
+// // while ( (event = fileReader.parseNextEvent()) != null) {
+// // System.out.println("Event = " + event.toString());
+// // }
+// }
+ }
+
+ t2 = System.currentTimeMillis();
+ System.out.println("Sequential Time = " + (t2-t1) + " milliseconds");
+
+ fileReader.rewind();
+
+ t1 = System.currentTimeMillis();
+
+ for (int i=1; i <= evCount; i++) {
+ event = fileReader.getEvent(i);
+ if (event == null) {
+ System.out.println("We got a NULL event !!!");
+ return;
+ }
+
+// if (i %10 == 0) {
+// System.out.println("Event #" + i + " =\n" + event);
+// int[] d = event.getIntData();
+// System.out.println("Data[0] = " + d[0] + ", Data[last] = " + d[d.length-1]);
+// }
+
+ }
+
+ t2 = System.currentTimeMillis();
+ System.out.println("Random access Time = " + (t2-t1) + " milliseconds");
+ }
+ catch (IOException e) {
+ e.printStackTrace();
+ }
+ catch (EvioException e) {
+ e.printStackTrace();
+ }
+ }
+
+
+ if (false) {
+ System.out.println("\nTRY READING");
+
+ File fileIn = new File(fileName);
+ System.out.println("read ev file: " + fileName + " size: " + fileIn.length());
+ try {
+ EvioReader fileReader = new EvioReader(fileName);
+// System.out.println("\nev count= " + fileReader.getEventCount());
+// System.out.println("dictionary = " + fileReader.getDictionaryXML() + "\n");
+//
+//
+// System.out.println("ev count = " + fileReader.getEventCount());
+ EvioEvent ev = fileReader.getEvent(1);
+ System.out.println("Event = \n" + ev);
+
+
+ }
+ catch (IOException e) {
+ e.printStackTrace();
+ }
+ catch (EvioException e) {
+ e.printStackTrace();
+ }
+ }
+
+
+ System.out.println("DONE READING");
+
+ }
+
+
+
}
jevio-base/src/main/java/org/jlab/coda/jevio/test
diff -u -r1.2 -r1.3
--- CompositeTester.java 8 Mar 2012 20:39:27 -0000 1.2
+++ CompositeTester.java 3 Apr 2013 20:07:47 -0000 1.3
@@ -246,14 +246,101 @@
try {
EvioEvent ev = new EvioEvent(0, DataType.COMPOSITE, 0);
+ ev.appendCompositeData(new CompositeData[] {cData});
+
+ // Write it to this file
+ String fileName = "./composite.dat";
+
+ EventWriter writer = new EventWriter(fileName);
+ writer.writeEvent(ev);
+ writer.close();
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ }
+
+
+ }
+
+
+ /**
+ * More complicated example of providing a format string and some data
+ * in order to create a CompositeData object.
+ */
+ public static void main(String args[]) {
+
+ // Create a CompositeData object ...
+
+ // Format to write a N shorts, 1 float, 1 double a total of N times
+ String format = "N(I,F)";
+
+System.out.println("format = " + format);
+
+ // Now create some data
+ CompositeData.Data myData1 = new CompositeData.Data();
+ myData1.addN(1);
+ myData1.addInt(1); // use array for convenience
+ myData1.addFloat(1.0F);
+
+ // Now create some data
+ CompositeData.Data myData2 = new CompositeData.Data();
+ myData2.addN(1);
+ myData2.addInt(2); // use array for convenience
+ myData2.addFloat(2.0F);
+
+ // Now create some data
+ CompositeData.Data myData3 = new CompositeData.Data();
+ myData3.addN(1);
+ myData3.addInt(3); // use array for convenience
+ myData3.addFloat(3.0F);
+
+
+System.out.println("Create composite data objects");
+
+ // Create CompositeData object
+ CompositeData[] cData = new CompositeData[3];
+ try {
+ cData[0] = new CompositeData(format, 1, myData1, 1 ,1);
+ cData[1] = new CompositeData(format, 2, myData2, 2 ,2);
+ cData[2] = new CompositeData(format, 3, myData3, 3 ,3);
+ }
+ catch (EvioException e) {
+ e.printStackTrace();
+ System.exit(-1);
+ }
+
+ // Print it out
+ System.out.println("Print composite data objects");
+ printCompositeDataObject(cData[0]);
+ printCompositeDataObject(cData[1]);
+ printCompositeDataObject(cData[2]);
+
+ try {
+ EvioEvent ev = new EvioEvent(0, DataType.COMPOSITE, 0);
ev.appendCompositeData(cData);
// Write it to this file
String fileName = "./composite.dat";
+System.out.println("WRITE FILE:");
EventWriter writer = new EventWriter(fileName);
writer.writeEvent(ev);
writer.close();
+
+ // Read it from file
+System.out.println("READ FILE & PRINT CONTENTS:");
+ EvioReader reader = new EvioReader(fileName);
+ EvioEvent evR = reader.parseNextEvent();
+ BaseStructureHeader h = evR.getHeader();
+ System.out.println("event: tag = " + h.getTag() +
+ ", type = " + h.getDataTypeName() + ", len = " + h.getLength());
+ if (evR != null) {
+ CompositeData[] cDataR = evR.getCompositeData();
+ for (CompositeData cd : cDataR) {
+ printCompositeDataObject(cd);
+ }
+ }
+
}
catch (Exception e) {
e.printStackTrace();
@@ -264,8 +351,9 @@
+
/** For testing only */
- public static void main(String args[]) {
+ public static void main4(String args[]) {
//create an event writer to write out the test events.
String fileName = "/daqfs/home/timmer/coda/evio-4.0.sergey/Linux-x86_64/bin/sample.dat";
@@ -306,6 +394,7 @@
type = types.get(i);
System.out.print(String.format("type = %9s, val = ", type));
switch (type) {
+ case NVALUE:
case INT32:
case UINT32:
case UNKNOWN32:
jevio-base/src/main/java/org/jlab/coda/jevio/graphics
diff -u -r1.2 -r1.3
--- EventTreePanel.java 8 Mar 2012 20:39:27 -0000 1.2
+++ EventTreePanel.java 3 Apr 2013 20:07:47 -0000 1.3
@@ -372,9 +372,14 @@
case COMPOSITE:
try {
- CompositeData cData = structure.getCompositeData();
+ CompositeData[] cData = structure.getCompositeData();
if (cData != null) {
- textArea.append(cData.toString(intsInHex));
+ for (int i=0; i < cData.length; i++) {
+ textArea.append("composite data object ");
+ textArea.append(i + ":\n");
+ textArea.append(cData[i].toString(intsInHex));
+ textArea.append("\n\n");
+ }
}
else {
textArea.append("null data\n");
jevio-base/src/main/java/org/jlab/coda/jevio/graphics
diff -u -r1.2 -r1.3
--- EventTreeMenu.java 20 Mar 2012 23:21:49 -0000 1.2
+++ EventTreeMenu.java 3 Apr 2013 20:07:47 -0000 1.3
@@ -5,7 +5,10 @@
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
+import java.util.EventListener;
+
import javax.swing.*;
+import javax.swing.event.EventListenerList;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.border.LineBorder;
import javax.swing.border.EmptyBorder;
@@ -108,7 +111,13 @@
/** Thread to update cMsg queue size in GUI. */
private UpdateThread cmsgUpdateThread;
-
+ //----------------------------
+ // General function
+ //----------------------------
+ /**
+ * Listener list for structures (banks, segments, tagsegments) encountered while processing an event.
+ */
+ private EventListenerList evioListenerList;
/**
* This class is a thread which updates the number of events existing in the queue
@@ -528,7 +537,7 @@
}
};
etItem.addActionListener(etListener);
- etItem.setEnabled(false); // turn off ET source for now
+ // etItem.setEnabled(false); // turn off ET source for now
ButtonGroup group = new ButtonGroup();
group.add(fileItem);
@@ -1130,6 +1139,8 @@
e.printStackTrace();
}
}
+ connectEvioListeners(); // Connect Listeners to the parser.
+
return evioFileReader;
}
@@ -1174,6 +1185,7 @@
evioFileReader = null;
e.printStackTrace();
}
+ connectEvioListeners(); // Connect Listeners to the parser.
return evioFileReader;
}
@@ -1242,5 +1254,39 @@
}
}
+ /**
+ * Add an Evio listener. Evio listeners listen for structures encountered when an event is being parsed.
+ * The listeners are passed to the EventParser once a file is opened.
+ * @param listener The Evio listener to add.
+ */
+ public void addEvioListener(IEvioListener listener) {
+
+ if (listener == null) {
+ return;
+ }
+
+ if (evioListenerList == null) {
+ evioListenerList = new EventListenerList();
+ }
+
+ evioListenerList.add(IEvioListener.class, listener);
+ }
+ /**
+ * Connect the listeners in the evioListenerList to the EventParser
+ */
+ private void connectEvioListeners(){
+
+ if (evioListenerList == null) {
+ return;
+ }
+
+ EventParser parser = getEvioFileReader().getParser();
+
+ EventListener listeners[] = evioListenerList.getListeners(IEvioListener.class);
+
+ for (int i = 0; i < listeners.length; i++) {
+ parser.addEvioListener((IEvioListener)listeners[i]);
+ }
+ }
-}
\ No newline at end of file
+}
CVSspam 0.2.12