Commit in jevio-base/src/main/java/org/jlab/coda/jevio on MAIN | |||
CompositeData.java | +300 | -123 | 1.7 -> 1.8 |
EvioDictionaryEntry.java | +1 | 1.3 -> 1.4 | |
BlockHeaderV2.java | +28 | -2 | 1.1 -> 1.2 |
EventWriter.java | +1279 | -311 | 1.4 -> 1.5 |
IEvioStructure.java | +3 | -11 | 1.3 -> 1.4 |
EvioEvent.java | +51 | -89 | 1.3 -> 1.4 |
ByteDataTransformer.java | +90 | -17 | 1.4 -> 1.5 |
IBlockHeader.java | +3 | -2 | 1.1 -> 1.2 |
DataType.java | -1 | 1.4 -> 1.5 | |
IEvioListener.java | +9 | -9 | 1.3 -> 1.4 |
EvioReader.java | +703 | -209 | 1.2 -> 1.3 |
EvioFileTest.java | +2 | -2 | 1.4 -> 1.5 |
StructureFinder.java | +170 | -33 | 1.3 -> 1.4 |
EvioFile.java | +1 | -1 | 1.4 -> 1.5 |
EventParser.java | +1 | -3 | 1.4 -> 1.5 |
Demo.java | +5 | -6 | 1.4 -> 1.5 |
EvioXMLDictionary.java | +716 | -102 | 1.3 -> 1.4 |
StructureTransformer.java | +12 | -12 | 1.1 -> 1.2 |
EventBuilder.java | +158 | -48 | 1.3 -> 1.4 |
BlockHeaderV4.java | +168 | -16 | 1.2 -> 1.3 |
BaseStructureHeader.java | +18 | -3 | 1.3 -> 1.4 |
BankHeader.java | +1 | -1 | 1.3 -> 1.4 |
BaseStructure.java | +681 | -95 | 1.6 -> 1.7 |
test/Tester.java | +166 | -26 | 1.2 -> 1.3 |
/FileTest.java | +527 | -319 | 1.1 -> 1.2 |
/CompositeTester.java | +90 | -1 | 1.2 -> 1.3 |
graphics/EventTreePanel.java | +7 | -2 | 1.2 -> 1.3 |
/EventTreeMenu.java | +49 | -3 | 1.2 -> 1.3 |
+5239 | -1447 |
update to new JEVIO version released 2012-11-1
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 */
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> {
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);
} /**
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]
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.
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.
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];
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;
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
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);
}
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]
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++; } }
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); + } + +
}
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 {
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.
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();
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(); }
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; } }
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. */
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."); }
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. *
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. */
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]
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;
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"); + + } + + +
}
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:
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");
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
+}
Use REPLY-ALL to reply to list
To unsubscribe from the LCD-CVS list, click the following link:
https://listserv.slac.stanford.edu/cgi-bin/wa?SUBED1=LCD-CVS&A=1