Print

Print


Commit in jevio-base/src/main/java/org/jlab/coda/jevio on MAIN
CompositeData.java+300-1231.7 -> 1.8
EvioDictionaryEntry.java+11.3 -> 1.4
BlockHeaderV2.java+28-21.1 -> 1.2
EventWriter.java+1279-3111.4 -> 1.5
IEvioStructure.java+3-111.3 -> 1.4
EvioEvent.java+51-891.3 -> 1.4
ByteDataTransformer.java+90-171.4 -> 1.5
IBlockHeader.java+3-21.1 -> 1.2
DataType.java-11.4 -> 1.5
IEvioListener.java+9-91.3 -> 1.4
EvioReader.java+703-2091.2 -> 1.3
EvioFileTest.java+2-21.4 -> 1.5
StructureFinder.java+170-331.3 -> 1.4
EvioFile.java+1-11.4 -> 1.5
EventParser.java+1-31.4 -> 1.5
Demo.java+5-61.4 -> 1.5
EvioXMLDictionary.java+716-1021.3 -> 1.4
StructureTransformer.java+12-121.1 -> 1.2
EventBuilder.java+158-481.3 -> 1.4
BlockHeaderV4.java+168-161.2 -> 1.3
BaseStructureHeader.java+18-31.3 -> 1.4
BankHeader.java+1-11.3 -> 1.4
BaseStructure.java+681-951.6 -> 1.7
test/Tester.java+166-261.2 -> 1.3
    /FileTest.java+527-3191.1 -> 1.2
    /CompositeTester.java+90-11.2 -> 1.3
graphics/EventTreePanel.java+7-21.2 -> 1.3
        /EventTreeMenu.java+49-31.2 -> 1.3
+5239-1447
28 modified files
update to new JEVIO version released 2012-11-1

jevio-base/src/main/java/org/jlab/coda/jevio
CompositeData.java 1.7 -> 1.8
diff -u -r1.7 -r1.8
--- CompositeData.java	21 Mar 2012 21:24:19 -0000	1.7
+++ CompositeData.java	3 Apr 2013 20:07:46 -0000	1.8
@@ -44,7 +44,6 @@
 import java.nio.ByteOrder;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.ListIterator;
 
 /**
  * This is the class defining the composite data type.
@@ -82,7 +81,7 @@
 	/** Buffer containing only the data of the composite item (no headers). */
 	private ByteBuffer dataBuffer;
 
-    /** Length of only data in bytes. */
+    /** Length of only data in bytes (not including padding). */
     private int dataBytes;
 
     /** Offset (in 32bit words) in rawBytes to place of data. */
@@ -273,6 +272,177 @@
 
 
     /**
+     * This method parses an array of raw bytes into an array of CompositeData objects.
+     *
+     * @param rawBytes  array of raw bytes to parse
+     * @param byteOrder byte order of raw bytes
+     * @return array of CompositeData objects obtained from parsing rawBytes. If none, return null.
+     * @throws EvioException if null args or bad format of raw data
+     */
+    static public CompositeData[] parse(byte[] rawBytes, ByteOrder byteOrder) throws EvioException {
+
+        boolean debug = false;
+
+        if (rawBytes == null) {
+            throw new EvioException("null argument(s)");
+        }
+
+        if (debug) System.out.println("CompositeData parse: incoming byte order = " + byteOrder);
+
+        if (byteOrder == null) {
+            byteOrder = ByteOrder.BIG_ENDIAN;
+        }
+
+        if (debug) System.out.println("Analyzing composite data:");
+
+        // List containing all parsed CompositeData objects
+        ArrayList<CompositeData> list = new ArrayList<CompositeData>(100);
+        // How many bytes have we read for the current CompositeData object?
+        int byteCount;
+        // Offset into the given rawBytes array for the current CompositeData object
+        int rawBytesOffset = 0;
+        // How many unused bytes are left in the given rawBytes array?
+        int rawBytesLeft = rawBytes.length;
+
+        if (debug) System.out.println("    raw byte count = " + rawBytesLeft);
+
+        // Parse while we still have bytes to read ...
+        while (rawBytesLeft > 0) {
+
+            byteCount = 0;
+
+            // Create and fill new CompositeData object
+            CompositeData cd = new CompositeData();
+            cd.byteOrder = byteOrder;
+
+            // First read the tag segment header
+            cd.tsHeader = EventParser.createTagSegmentHeader(rawBytes, rawBytesOffset, byteOrder);
+            byteCount += 4*(cd.tsHeader.getLength() + 1);
+
+            if (debug) {
+                System.out.println("    tagseg: type = " + cd.tsHeader.getDataType() +
+                                    ", tag = " + cd.tsHeader.getTag() + ", len = " + cd.tsHeader.getLength());
+            }
+
+            // Hop over tagseg header
+            cd.dataOffset = cd.tsHeader.getHeaderLength();
+
+            // Read the format string it contains
+            String[] strs = BaseStructure.unpackRawBytesToStrings(rawBytes, rawBytesOffset + 4*cd.dataOffset);
+
+            if (strs.length < 1) {
+               throw new EvioException("bad format string data");
+            }
+            cd.format = strs[0];
+
+            if (debug) {
+                System.out.println("    format: " + cd.format);
+            }
+
+            // Chew on format string & spit out array of ints
+            cd.formatInts = compositeFormatToInt(cd.format);
+            if (cd.formatInts.size() < 1) {
+               throw new EvioException("bad format string data");
+            }
+
+            // Hop over tagseg (format string) data
+            cd.dataOffset = cd.tsHeader.getLength() + 1;
+
+            // Read the data bank header
+            cd.bHeader = EventParser.createBankHeader(rawBytes, rawBytesOffset + 4*cd.dataOffset, byteOrder);
+            byteCount += 4*(cd.bHeader.getLength() + 1);
+
+            // Hop over bank header
+            cd.dataOffset += cd.bHeader.getHeaderLength();
+
+            // How many bytes do we skip over at the end?
+            int padding = cd.bHeader.getPadding();
+
+            // How much real data do we have (without padding)?
+            cd.dataBytes = 4*(cd.bHeader.getLength() - (cd.bHeader.getHeaderLength() - 1)) - padding;
+            if (cd.dataBytes < 2) {
+               throw new EvioException("no composite data");
+            }
+
+            if (debug) {
+                System.out.println("    bank: type = " + cd.bHeader.getDataType() +
+                                    ", tag = " + cd.bHeader.getTag() + ", num = " + cd.bHeader.getNumber());
+                System.out.println("    bank: len (words) = " + cd.bHeader.getLength() +
+                                   ", data len - padding (bytes) = " + cd.dataBytes);
+            }
+
+            // Make copy of only the rawbytes for this CompositeData object (including padding)
+            cd.rawBytes = new byte[byteCount];
+            System.arraycopy(rawBytes, rawBytesOffset, cd.rawBytes, 0, byteCount);
+
+            // Put only actual data into ByteBuffer object
+            cd.dataBuffer = ByteBuffer.wrap(cd.rawBytes, 4*cd.dataOffset, cd.dataBytes).slice();
+
+            // Turn dataBuffer into list of items and their types
+            cd.process();
+
+            // Add to this CompositeData object to list
+            list.add(cd);
+
+            // Track how many raw bytes we have left to parse
+            rawBytesLeft   -= byteCount;
+
+            // Offset into rawBytes of next CompositeData object
+            rawBytesOffset += byteCount;
+            if (debug) System.out.println("    raw byte count = " + rawBytesLeft + ", raw byte offset = " +
+                                            rawBytesOffset);
+        }
+
+        int size = list.size();
+        if (size > 0) {
+            // Turn list into array
+            CompositeData[] cdArray = new CompositeData[size];
+            return list.toArray(cdArray);
+        }
+
+        return null;
+    }
+
+    /**
+     * This method generates raw bytes of evio format from an array of CompositeData objects.
+     * The returned array consists of gluing together all the individual objects' rawByte arrays.
+     *
+     * @param data  array of CompositeData object to turn into bytes
+     * @return array of raw, evio format bytes; null if arg is null or empty
+     * @throws EvioException if data takes up too much memory to store in raw byte array (JVM limit)
+     */
+    static public byte[] generateRawBytes(CompositeData data[]) throws EvioException {
+
+        if (data == null || data.length < 1) {
+            return null;
+        }
+
+        // Get a total length (# bytes)
+        int totalLen = 0, len;
+        for (CompositeData cd : data) {
+            len = cd.getRawBytes().length;
+            if (Integer.MAX_VALUE - totalLen < len) {
+                throw new EvioException("added data overflowed containing structure");
+            }
+            totalLen += len;
+        }
+
+        // Allocate an array
+        byte[] rawBytes = new byte[totalLen];
+
+        // Copy everything in
+        int offset = 0;
+        for (CompositeData cd : data) {
+            len = cd.getRawBytes().length;
+            System.arraycopy(cd.getRawBytes(), 0, rawBytes, offset, len);
+            offset += len;
+        }
+
+        return rawBytes;
+    }
+
+
+    /**
      * Method to clone a CompositeData object.
      * Deep cloned so no connection exists between
      * clone and object cloned.
@@ -363,19 +533,7 @@
          *  of being part of the format string. */
         private ArrayList<Integer>  nList = new ArrayList<Integer>(100);
 
-//        private String format;
-//        private List<Integer> formatInts;
-//        private int formatIndex;
-//        private int parenLevel;
-
-//        /** The number of times items of a particular type get added. */
-//        private int repeat = 1;
-
-//        public Data(String format) throws EvioException {
-//            this.format = format;
-//            this.formatInts = compositeFormatToInt(format);
-//            if (formatInts.size() <= 0) throw new EvioException("empty format list");
-//        }
+
 
         /** Constructor. */
         public Data() {}
@@ -409,30 +567,18 @@
         }
 
         /**
-         * This method add an "N" or multiplier value to the data
-         * in the sequence determined by the format string.
-         * Does not needed to be added in sequence with other data.
+         * This method adds an "N" or multiplier value to the data.
+         * It needs to be added in sequence with other data.
          * @param N  N or multiplier value
          */
         synchronized public void addN(int N) {
             nList.add(N);
+            dataItems.add(N);
+            dataTypes.add(DataType.INT32);
             addBytesToData(4);
         }
 
 
-        /**
-         * This method add an array of "N" or multiplier values to the data
-         * in the sequence determined by the format string. Does not needed
-         * to be added in sequence with other data.
-         * @param N  array of N or multiplier values
-         */
-        synchronized public void addN(int[] N) {
-            for (int n : N) {
-                nList.add(n);
-                addBytesToData(4);
-            }
-        }
-
 
         /**
          * Add a signed 32 bit integer to the data.
@@ -1336,7 +1482,8 @@
      * This method converts (swaps) a buffer of EVIO composite type between big &
      * little endian. It swaps the entire type including the beginning tagsegment
      * header, the following format string it contains, the data's bank header,
-     * and finally the data itself.
+     * and finally the data itself. The src array may contain an array of
+     * composite type items and all will be swapped.
      *
      * @param src      source data array (of 32 bit words)
      * @param srcOff   # of bytes offset into source data array
@@ -1346,7 +1493,8 @@
      * @param srcOrder the byte order of data in src
      *
      * @throws EvioException if offsets or length < 0; if src = null;
-     *                       if src = dest and offsets are not the same
+     *                       if src = dest and offsets are not the same;
+     *                       if src or dest is too small
      */
     public static void swapAll (byte[] src, int srcOff, byte[] dest, int destOff,
                                 int length, ByteOrder srcOrder) throws EvioException {
@@ -1373,93 +1521,117 @@
         ByteOrder destOrder = (srcOrder == ByteOrder.BIG_ENDIAN) ?
                                ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN;
 
-        // Move to beginning of tagseg header
-        int srcDataOffset  = srcOff;
-        int destDataOffset = destOff;
-        //System.out.println("srcDataOffset = " + srcDataOffset + ", len (Bytes) = " + (4*length) + ", src array len = " + src.length);
-        ByteBuffer  srcBuffer = ByteBuffer.wrap( src,  srcDataOffset, 4*length);
-        ByteBuffer destBuffer = ByteBuffer.wrap(dest, destDataOffset, 4*length);
+
+        // How many unused bytes are left in the src array?
+        int srcBytesLeft = 4*length;
+
+        // How many bytes taken for this CompositeData object?
+        int dataOffset;
+
+        // Wrap input & output arrays in ByteBuffers for convenience
+        ByteBuffer  srcBuffer = ByteBuffer.wrap( src, srcOff,  4*length);
+        ByteBuffer destBuffer = ByteBuffer.wrap(dest, destOff, 4*length);
 
         // Here is where we do some of the swapping
         srcBuffer.order(srcOrder);
         destBuffer.order(destOrder);
 
-        // First read the tag segment header
-        TagSegmentHeader tsHeader = EventParser.createTagSegmentHeader(src, srcDataOffset, srcOrder);
-        int headerLen  = tsHeader.getHeaderLength();
-        int dataLength = tsHeader.getLength() - (headerLen - 1);
+        while (srcBytesLeft > 0) {
 
-//System.out.println("theaderLen = "+headerLen+ ", theader.getLn() = " +
-//                                   tsHeader.getLength()+ ", tdataLen = " + dataLength);
+            // Start here
+            dataOffset = 0;
+//System.out.println("start src offset = " + (srcOff + dataOffset));
 
-        // Oops, no format data
-        if (dataLength < 1) {
-           throw new EvioException("no format data");
-        }
+            // First read the tag segment header
+            TagSegmentHeader tsHeader = EventParser.createTagSegmentHeader(src, srcOff + dataOffset, srcOrder);
+            int headerLen  = tsHeader.getHeaderLength();
+            int dataLength = tsHeader.getLength() - (headerLen - 1);
 
-        // Got all we needed from the tagseg header, now swap as it's written out.
-        tsHeader.write(destBuffer);
+//System.out.println("tag len = " + tsHeader.getLength() + ", dataLen = " + dataLength);
 
-        // Move to beginning of string data
-        srcDataOffset  += 4*headerLen;
-        destDataOffset += 4*headerLen;
+            // Oops, no format data
+            if (dataLength < 1) {
+               throw new EvioException("no format data");
+            }
 
-        // Read the format string it contains
-        String[] strs = BaseStructure.unpackRawBytesToStrings(src, srcDataOffset);
+            // Got all we needed from the tagseg header, now swap as it's written out.
+            tsHeader.write(destBuffer);
 
-        if (strs.length < 1) {
-           throw new EvioException("bad format string data");
-        }
-        String format = strs[0];
+            // Move to beginning of string data
+            dataOffset += 4*headerLen;
 
-        // Transform string format into int array format
-        List<Integer> formatInts = compositeFormatToInt(format);
-        if (formatInts.size() < 1) {
-           throw new EvioException("bad format string data");
-        }
+            // Read the format string it contains
+            String[] strs = BaseStructure.unpackRawBytesToStrings(src, srcOff + dataOffset);
 
-        // Char data does not get swapped but needs
-        // to be copied if not swapping in place.
-        if (!inPlace) {
-            System.arraycopy(src, srcDataOffset, dest, destDataOffset, 4*dataLength);
-        }
+            if (strs.length < 1) {
+               throw new EvioException("bad format string data");
+            }
+            String format = strs[0];
 
-        // Move to beginning of bank header
-        srcDataOffset  += 4*dataLength;
-        destDataOffset += 4*dataLength;
+            // Transform string format into int array format
+            List<Integer> formatInts = compositeFormatToInt(format);
+            if (formatInts.size() < 1) {
+               throw new EvioException("bad format string data");
+            }
 
-        // Read the data bank header
-        BankHeader bHeader = EventParser.createBankHeader(src, srcDataOffset, srcOrder);
-        headerLen  = bHeader.getHeaderLength();
-        dataLength = bHeader.getLength() - (headerLen - 1);
-
-//System.out.println("bheaderLen = "+headerLen+ ", bheader.getLn() = " +
-//                           bHeader.getLength()+ ", bdataLen = " + dataLength);
-
-        // Oops, no data
-        if (dataLength < 1) {
-           throw new EvioException("no data");
-        }
-
-        // Adjust data length by switching units from
-        // ints to bytes and accounting for padding.
-        dataLength = 4*dataLength - bHeader.getPadding();
-
-        // Got all we needed from the bank header, now swap as it's written out.
-        destBuffer.position(destDataOffset);
-        bHeader.write(destBuffer);
-
-        // Move to beginning of data
-        srcDataOffset  += 4*headerLen;
-        destDataOffset += 4*headerLen;
-        srcBuffer.position(srcDataOffset);
-        destBuffer.position(destDataOffset);
+            // Char data does not get swapped but needs
+            // to be copied if not swapping in place.
+            if (!inPlace) {
+                System.arraycopy(src,   srcOff + dataOffset,
+                                 dest, destOff + dataOffset, 4*dataLength);
+            }
+
+            // Move to beginning of bank header
+            dataOffset += 4*dataLength;
+
+            // Read the data bank header
+            BankHeader bHeader = EventParser.createBankHeader(src, srcOff + dataOffset, srcOrder);
+            headerLen  = bHeader.getHeaderLength();
+            dataLength = bHeader.getLength() - (headerLen - 1);
+
+//System.out.println("bank len = " + bHeader.getLength() + ", dataLen = " + dataLength);
+
+            // Oops, no data
+            if (dataLength < 1) {
+               throw new EvioException("no data");
+            }
+
+            // Adjust data length by switching units from
+            // ints to bytes and accounting for padding.
+            dataLength = 4*dataLength - bHeader.getPadding();
+
+            // Got all we needed from the bank header, now swap as it's written out.
+            destBuffer.position(destOff + dataOffset);
+            bHeader.write(destBuffer);
+
+            // Move to beginning of data
+            dataOffset += 4*headerLen;
+            srcBuffer.position(  srcOff + dataOffset);
+            destBuffer.position(destOff + dataOffset);
+
+            // Swap data
+            swapData(srcBuffer, destBuffer, dataLength, formatInts);
+
+            // Set buffer positions and offset
+            dataOffset += dataLength;
+            srcBuffer.position( srcOff + dataOffset);
+            destBuffer.position(srcOff + dataOffset);
+
+            srcOff       += dataOffset;
+            destOff      += dataOffset;
+            srcBytesLeft -= dataOffset;
+//System.out.println("bytes left = " + srcBytesLeft);
+//System.out.println("src pos = " + srcBuffer.position() + ", dest pos = " + destBuffer.position());
 
-        // Swap data
-        swapData(srcBuffer, destBuffer, dataLength, formatInts);
+            // Oops, things aren't coming out evenly
+            if (srcBytesLeft < 0) {
+               throw new EvioException("bad format");
+            }
+        }
     }
 
 
+
     /**
      * This method converts (swaps) an array of EVIO composite type data
      * between IEEE (big endian) and DECS (little endian). This
@@ -1475,8 +1647,10 @@
      * @param ifmt     format list as produced by {@link #compositeFormatToInt(String)}
      * @param srcOrder byte order of the src data array
      *
-     * @throws EvioException if src == null; if nBytes or nfmt args < 0;
-     *                       if src = dest and offsets are not the same
+     * @throws EvioException if src == null or ifmt == null;
+     *                       if nBytes or ifmt size <= 0;
+     *                       if src = dest and offsets are not the same;
+     *                       if src or dest is too small
      */
     public static void swapData(byte[] src, int srcOff, byte[] dest, int destOff,
                                 int nBytes, List<Integer> ifmt, ByteOrder srcOrder)
@@ -1523,8 +1697,8 @@
      * @param nBytes   length of data array in bytes
      * @param ifmt     format list as produced by {@link #compositeFormatToInt(String)}
      *
-     * @throws EvioException if nBytes or nfmt args < 0 ; if srcBuf or destBuf is too
-     *                       small
+     * @throws EvioException if ifmt null; ifmt size or nBytes <= 0;
+     *                       srcBuf or destBuf is too small
      */
     public static void swapData(ByteBuffer srcBuf, ByteBuffer destBuf,
                                 int nBytes, List<Integer> ifmt)
@@ -1532,8 +1706,6 @@
 
         boolean debug = false;
         int imt, ncnf, kcnf, lev, iterm;
-        // size of int list
-        int nfmt = ifmt.size();
 
         class LV {
           int left;    // index of ifmt[] element containing left parenthesis "("
@@ -1543,7 +1715,11 @@
         };
 
         // check args
-        if (nBytes <= 0 || nfmt <= 0) throw new EvioException("bad argument value(s)");
+        if (ifmt == null || nBytes <= 0) throw new EvioException("bad argument value(s)");
+
+        // size of int list
+        int nfmt = ifmt.size();
+        if (nfmt <= 0) throw new EvioException("empty format list");
 
         if (destBuf == null) destBuf = srcBuf;
         boolean inPlace = (srcBuf == destBuf);
@@ -1805,8 +1981,8 @@
      * @param data     data to convert to raw bytes
      * @param ifmt     format list as produced by {@link #compositeFormatToInt(String)}
      *
-     * @throws EvioException if nBytes or nfmt args < 0 ; if srcBuf or destBuf is too
-     *                       small
+     * @throws EvioException if ifmt size <= 0; if srcBuf or destBuf is too
+     *                       small; not enough dataItems for the given format
      */
     public static void dataToRawBytes(ByteBuffer rawBuf, CompositeData.Data data,
                                       List<Integer> ifmt)
@@ -1826,7 +2002,6 @@
         if (ifmt == null || data == null || rawBuf == null) throw new EvioException("arg is null");
 
         // size of format list
-        // TODO: bug, ifmt may be NULLLLLL in other METHODS ......
         int nfmt = ifmt.size();
         if (nfmt <= 0) throw new EvioException("empty format list");
 
@@ -1840,8 +2015,6 @@
         lev   = 0;  // parenthesis level
         iterm = 0;
 
-        ListIterator<Integer> nListIter = data.nList.listIterator();
-
         int itemIndex = 0;
         int itemCount = data.dataItems.size();
 
@@ -1890,10 +2063,10 @@
                         kcnf = 0;
 
                         // get "N" value from List
-                        if (!nListIter.hasNext()) {
-                            throw new EvioException("Not enough N values provided");
+                        if (data.dataTypes.get(itemIndex) != DataType.INT32) {
+                            throw new EvioException("Data type mismatch");
                         }
-                        ncnf = nListIter.next();
+                        ncnf = (Integer)data.dataItems.get(itemIndex++);
 if (debug) System.out.println("N 1 from list = " + ncnf);
 
                         // put into buffer (relative put)
@@ -1936,10 +2109,10 @@
             // if 'ncnf' is zero, get "N" from list (always in 'int' format)
             if (ncnf == 0) {
                 // get "N" value from List
-                if (!nListIter.hasNext()) {
-                    throw new EvioException("Not enough N values provided");
+                if (data.dataTypes.get(itemIndex) != DataType.INT32) {
+                    throw new EvioException("Data type mismatch");
                 }
-                ncnf = nListIter.next();
+                ncnf = (Integer)data.dataItems.get(itemIndex++);
 
                 // put into buffer (relative put)
                 rawBuf.putInt(ncnf);
@@ -1954,6 +2127,7 @@
             //     ncnf - how many times format repeated
 
             // Convert data type kcnf
+if (debug) System.out.println("Convert data of type = " + kcnf + ", itemIndex = " + itemIndex);
             switch (kcnf) {
                 // 64 Bits
                 case 8:
@@ -2090,7 +2264,7 @@
      */
     public void process() {
 
-        boolean debug=false;
+        boolean debug = false;
         int imt, ncnf, kcnf, lev, iterm;
 
         // size of int list
@@ -2412,6 +2586,9 @@
                 case FLOAT32:
                     sb.append("F="); sb.append(getFloat());
                     break;
+                case NVALUE:
+                    sb.append("N="); sb.append(getNValue());
+                    break;
                 case CHARSTAR8:
                     sb.append("a=");
                     String[] strs = getStrings();
@@ -2941,9 +3118,9 @@
 
     /**
      * This method returns a string representation of this CompositeData object
-     * suitable for displaying in EventTreeFrame gui. Each data item is separated
-     * from those before and after by a line. Non-parenthesis repeats are printed
-     * together.
+     * suitable for displaying in {@link org.jlab.coda.jevio.graphics.EventTreeFrame} gui.
+     * Each data item is separated from those before and after by a line.
+     * Non-parenthesis repeats are printed together.
      *
      * @param hex if <code>true</code> then print integers in hexadecimal
      */

jevio-base/src/main/java/org/jlab/coda/jevio
EvioDictionaryEntry.java 1.3 -> 1.4
diff -u -r1.3 -r1.4
--- EvioDictionaryEntry.java	28 Feb 2012 19:41:36 -0000	1.3
+++ EvioDictionaryEntry.java	3 Apr 2013 20:07:46 -0000	1.4
@@ -6,6 +6,7 @@
  * An entry into the evio dictionary.
  * 
  * @author heddle
+ * @deprecated
  * 
  */
 public class EvioDictionaryEntry implements Comparable<EvioDictionaryEntry> {

jevio-base/src/main/java/org/jlab/coda/jevio
BlockHeaderV2.java 1.1 -> 1.2
diff -u -r1.1 -r1.2
--- BlockHeaderV2.java	28 Feb 2012 19:41:36 -0000	1.1
+++ BlockHeaderV2.java	3 Apr 2013 20:07:46 -0000	1.2
@@ -167,6 +167,31 @@
 
 
     /**
+     * This copy constructor creates an evio version 1-3 BlockHeader
+     * from another object of this class.
+     * @param blkHeader block header object to copy
+     */
+    public BlockHeaderV2(BlockHeaderV2 blkHeader) {
+        if (blkHeader == null) {
+            return;
+        }
+        size         = blkHeader.size;
+        number       = blkHeader.number;
+        headerLength = blkHeader.headerLength;
+        version      = blkHeader.version;
+        end          = blkHeader.end;
+        start        = blkHeader.start;
+        reserved1    = blkHeader.reserved1;
+        magicNumber  = blkHeader.magicNumber;
+        bufferStartingPosition = blkHeader.bufferStartingPosition;
+    }
+
+    /*** {@inheritDoc}  */
+    public Object clone() {
+        return new BlockHeaderV2(this);
+    }
+
+    /**
      * Gets whether this block's first event is an evio dictionary.
      * This is not implemented in evio versions 1-3. Just return false.
      *
@@ -460,7 +485,8 @@
 	/**
 	 * Gives the bytes remaining in this block (physical record) given a buffer position. The position is an absolute
 	 * position in a byte buffer. This assumes that the absolute position in <code>bufferStartingPosition</code> is
-	 * being maintained properly by the reader.
+	 * being maintained properly by the reader. No block is longer than 2.1GB - 31 bits of length. This is for
+     * practical reasons - so a block can be read into a single byte array.
 	 *
 	 * @param position the absolute current position is a byte buffer.
 	 * @return the number of bytes remaining in this block (physical record.)
@@ -477,7 +503,7 @@
 			throw new EvioException("Provided position beyond buffer end position.");
 		}
 
-		return nextBufferStart - position;
+		return (int)(nextBufferStart - position);
 	}
 
 	/**

jevio-base/src/main/java/org/jlab/coda/jevio
EventWriter.java 1.4 -> 1.5
diff -u -r1.4 -r1.5
--- EventWriter.java	20 Mar 2012 23:21:49 -0000	1.4
+++ EventWriter.java	3 Apr 2013 20:07:46 -0000	1.5
@@ -1,11 +1,10 @@
 package org.jlab.coda.jevio;
 
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
+import java.io.*;
+import java.nio.BufferUnderflowException;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
+import java.nio.channels.FileChannel;
 import java.util.LinkedList;
 import java.util.BitSet;
 
@@ -20,39 +19,94 @@
 public class EventWriter {
 
     /**
-     * Offset to where the block length is written in the byte buffer, which always has a physical
-     * record header at the top.
+	 * This <code>enum</code> denotes the status of a read. <br>
+	 * SUCCESS indicates a successful read/write. <br>
+	 * END_OF_FILE indicates that we cannot read because an END_OF_FILE has occurred. Technically this means that what
+	 * ever we are trying to read is larger than the buffer's unread bytes.<br>
+	 * EVIO_EXCEPTION indicates that an EvioException was thrown during a read, possibly due to out of range values,
+	 * such as a negative start position.<br>
+     * CANNOT_OPEN_FILE  that we cannot write because the destination file cannot be opened.<br>
+	 * UNKNOWN_ERROR indicates that an unrecoverable error has occurred.
+	 */
+	public static enum IOStatus {
+		SUCCESS, END_OF_FILE, EVIO_EXCEPTION, CANNOT_OPEN_FILE, UNKNOWN_ERROR
+	}
+
+
+    /**
+     * Offset to where the block length is written in the byte buffer,
+     * which always has a physical record header at the top.
      */
     private static int BLOCK_LENGTH_OFFSET = 0;
 
     /**
-     * Offset to where the header length is written in the byte buffer, which always has a physical
-     * record header at the top.
+     * Offset to where the block number is written in the byte buffer,
+     * which always has a physical record header at the top.
+     */
+    private static int BLOCK_NUMBER_OFFSET = 4;
+
+    /**
+     * Offset to where the header length is written in the byte buffer,
+     * which always has a physical record header at the top.
      */
     private static int HEADER_LENGTH_OFFSET = 8;
 
     /**
-     * Offset to where the event count is written in the byte buffer, which always has a physical
-     * record header at the top.
+     * Offset to where the event count is written in the byte buffer,
+     * which always has a physical record header at the top.
      */
     private static int EVENT_COUNT_OFFSET = 12;
 
     /**
-     * Offset to where the bit information is written in the byte buffer, which always has a physical
-     * record header at the top.
+     * Offset to where the first reserved workd is written in the byte buffer,
+     * which always has a physical record header at the top.
+     */
+    private static int RESERVED1_COUNT_OFFSET = 16;
+
+    /**
+     * Offset to where the bit information is written in the byte buffer,
+     * which always has a physical record header at the top.
      */
     private static int BIT_INFO_OFFSET = 20;
 
-	/**
-	 * The default maximum size for a single block used for writing, in ints (2^18).
-     * This gives block sizes of 1,048,576 bytes (2^20) or about 1MB.
-	 */
-    private static int DEFAULT_BLOCK_SIZE_LIMIT = 262144;
+    /**
+     * Offset to where the magic number is written in the byte buffer,
+     * which always has a physical record header at the top.
+     */
+    private static final int MAGIC_OFFSET = 28;
+
+    /** Mask to get version number from 6th int in block. */
+    private static final int VERSION_MASK = 0xff;
 
     /**
-     * The default maximum event count for a single block used for writing.
+     * The default maximum size for a single block used for writing, in ints.
+     * This gives block sizes of about 4MB. It is a soft limit since a single
+     * event larger than this limit may need to be written.
      */
-    private static int DEFAULT_BLOCK_COUNT_LIMIT = 200;
+    private static int DEFAULT_BLOCK_SIZE = 1024000;
+
+    /** The default maximum event count for a single block used for writing. */
+    private static int DEFAULT_BLOCK_COUNT = 10000;
+
+    /**
+     * The upper limit of maximum size for a single block used for writing,
+     * in ints. This gives block sizes of about 40MB. It is a soft limit since
+     * a single event larger than this limit may need to be written.
+     */
+    private static int MAX_BLOCK_SIZE = 10240000;
+
+    /** The upper limit of maximum event count for a single block used for writing. */
+    private static int MAX_BLOCK_COUNT = 100000;
+
+    /**
+     * The lower limit of maximum size for a single block used for writing,
+     * in ints. This gives block sizes of about 400 bytes. It is a soft limit since
+     * a single event larger than this limit may need to be written.
+     */
+    private static int MIN_BLOCK_SIZE = 100;
+
+    /** The lower limit of maximum event count for a single block used for writing. */
+    private static int MIN_BLOCK_COUNT = 1;
 
 
 
@@ -60,20 +114,18 @@
      * Maximum block size for a single block (block header & following events).
      * There may be exceptions to this limit if the size of an individual
      * event exceeds this limit.
-     * Default is DEFAULT_BLOCK_SIZE_LIMIT.
+     * Default is {@link #DEFAULT_BLOCK_SIZE}.
      */
     private int blockSizeMax;
 
     /**
      * Maximum number of events in a block (events following a block header).
-     * Default is DEFAULT_BLOCK_COUNT_LIMIT.
+     * Default is {@link #DEFAULT_BLOCK_COUNT}.
      */
     private int blockCountMax;
 
-    /**
-     * Running count of the block number.
-     */
-    private int blockNumber = 0;
+    /** Running count of the block number. */
+    private int blockNumber;
 
     /**
      * Size in bytes needed to write all the events in the <code>eventBufferHoldingList</code>
@@ -87,9 +139,7 @@
      */
     private String xmlDictionary;
 
-    /**
-     * The next write will include an xml dictionary.
-     */
+    /** The next write will include an xml dictionary. */
     private boolean writeDictionary;
 
     /**
@@ -101,39 +151,46 @@
      */
     private BitSet bitInfo;
 
-    /**
-     * <code>True</code> if {@link #close()} was called, else <code>false</code>.
-     */
+    /** <code>True</code> if {@link #close()} was called, else <code>false</code>. */
     private boolean closed;
 
-    /**
-     * <code>True</code> if writing to file, else <code>false</code>.
-     */
+    /** <code>True</code> if writing to file, else <code>false</code>. */
     private boolean toFile;
 
-    /**
-     * Version 4 block header reserved int 1. Used by CODA for source ID in event building.
-     */
+    /** <code>True</code> if appending to file/buffer, <code>false</code> if (over)writing. */
+    private boolean append;
+
+    /** <code>True</code> if a last, empty block header has already been written. */
+    private boolean lastBlockOut;
+
+    /** Version 4 block header reserved int 1. Used by CODA for source ID in event building. */
     private int reserved1;
 
-    /**
-     * Version 4 block header reserved int 2.
-     */
+    /** Version 4 block header reserved int 2. */
     private int reserved2;
 
+    /** Keep tabs on where we initially started. For files this is 0. */
+    private int initialPosition;
+
+    /** Number of events written to buffer or file (although may not be flushed yet). */
+    private int eventsWritten;
+
+    /** Contains an empty, last block header which is placed at the file/buffer end
+     *  after each write. */
+    private ByteBuffer emptyLastHeader;
+
     //-----------------------
     // File related members
     //-----------------------
 
-    /**
-     * The byte order in which to write a file.
-     */
+    /** The byte order in which to write a file. */
     private ByteOrder byteOrder;
 
-	/**
-	 * The output stream used for writing a file.
-	 */
-	private FileOutputStream fileOutputStream;
+    /** The output stream used for writing a file. */
+    private FileOutputStream fileOutputStream;
+
+    /** The file channel, used for writing a file, derived from the output stream. */
+    private FileChannel fileChannel;
 
     /**
      * This is an internal byte buffer corresponding to one block (physical record). When this gets full it
@@ -153,19 +210,12 @@
     // Buffer related members
     //-----------------------
 
-    /**
-     * The output buffer when writing to a buffer.
-     */
+    /** The output buffer when writing to a buffer. */
     private ByteBuffer buffer;
 
-    /**
-     * When writing to a buffer, keep tabs on the front of the last header written.
-     */
+    /** When writing to a buffer, keep tabs on the front of the last header written. */
     private int bufferHeaderPosition;
 
-    /** Is this the first write into a buffer? */
-    private boolean firstWrite = true;
-
     /**
      * List used to store events (in EvioBank form) temporarily before writing them to a buffer.
      * This is done to accumulate events while keeping track of the total memory needed to write them.
@@ -176,131 +226,241 @@
 
 
     
+    //---------------------------------------------
+    // FILE Constructors
+    //---------------------------------------------
 
     /**
-     * Creates an event writer for writing to a file in native byte order.
-     * WARNING: If the file already exists, an attempt will be made to overwrite it.
-     * Appending to an existing event file is not supported.
-     *
-     * @param file the file to write to. Will be created.<br>
-     *             WARNING: If the file already exists, an attempt will be made to overwrite it.
-     *             Appending to an existing event file is not supported.
+     * Creates an <code>EventWriter</code> for writing to a file in native byte order.
+     * If the file already exists, its contents will be overwritten.
+     * If it doesn't exist, it will be created.
+     *
+     * @param file the file object to write to.<br>
      * @throws EvioException block size too small or file cannot be created
-      */
+     */
     public EventWriter(File file) throws EvioException {
-        this(file, DEFAULT_BLOCK_SIZE_LIMIT, DEFAULT_BLOCK_COUNT_LIMIT,
-             ByteOrder.nativeOrder(), null, null);
+        this(file, false);
+    }
+
+    /**
+     * Creates an <code>EventWriter</code> for writing to a file in native byte order.
+     * If the file already exists, its contents will be overwritten unless
+     * it is being appended to. If it doesn't exist, it will be created.
+     *
+     * @param file     the file object to write to.<br>
+     * @param append   if <code>true</code> and the file already exists,
+     *                 all events to be written will be appended to the
+     *                 end of the file.
+     *
+     * @throws EvioException block size too small or file cannot be created
+     */
+    public EventWriter(File file, boolean append) throws EvioException {
+        this(file, DEFAULT_BLOCK_SIZE, DEFAULT_BLOCK_COUNT,
+             ByteOrder.nativeOrder(), null, null, true, append);
     }
 
     /**
-     * Creates an event writer for writing to a file in native byte order.
-     * WARNING: If the file already exists, an attempt will be made to overwrite it.
-     * Appending to an existing event file is not supported.
+     * Creates an <code>EventWriter</code> for writing to a file in native byte order.
+     * If the file already exists, its contents will be overwritten unless
+     * it is being appended to. If it doesn't exist, it will be created.
+     *
+     * @param file       the file object to write to.<br>
+     * @param dictionary dictionary in xml format or null if none.
+     * @param append     if <code>true</code> and the file already exists,
+     *                   all events to be written will be appended to the
+     *                   end of the file.
      *
-     * @param filename the file to write to. Will be created.<br>
-     *                 WARNING: If the file already exists, an attempt will be made to overwrite it.
-     *                 Appending to an existing event file is not supported.
      * @throws EvioException block size too small or file cannot be created
-      */
+     */
+    public EventWriter(File file, String dictionary, boolean append) throws EvioException {
+        this(file, DEFAULT_BLOCK_SIZE, DEFAULT_BLOCK_COUNT,
+             ByteOrder.nativeOrder(), dictionary, null, true, append);
+    }
+
+    /**
+     * Creates an <code>EventWriter</code> for writing to a file in native byte order.
+     * If the file already exists, its contents will be overwritten.
+     * If it doesn't exist, it will be created.
+     *
+     * @param filename name of the file to write to.<br>
+     * @throws EvioException block size too small or file cannot be created
+     */
     public EventWriter(String filename) throws EvioException {
-        this(new File(filename), DEFAULT_BLOCK_SIZE_LIMIT, DEFAULT_BLOCK_COUNT_LIMIT,
-             ByteOrder.nativeOrder(), null, null);
+        this(filename, false);
     }
 
     /**
-     * Create an <code>EventWriter</code> for writing events to a file.
-     * WARNING: If the file already exists, an attempt will be made to overwrite it.
-     * Appending to an existing event file is not supported.
+     * Creates an <code>EventWriter</code> for writing to a file in native byte order.
+     * If the file already exists, its contents will be overwritten unless
+     * it is being appended to. If it doesn't exist, it will be created.
      *
+     * @param filename name of the file to write to.<br>
+     * @param append   if <code>true</code> and the file already exists,
+     *                 all events to be written will be appended to the
+     *                 end of the file.
      *
-     * @param file the file to write to. Will be created.<br>
-     *             WARNING: If the file already exists, an attempt will be made to overwrite it.
-     *             Appending to an existing event file is not supported.
-     * @param blockSizeMax the max blocksize to use which must be >= 1K and <= 1M ints.
-     *                     The size of the block will not be larger than this size.
-     *                     The one exception is when a single event being written is larger
-     *                     than the max size.
-     * @param blockCountMax the max number of events in a single block which must be
-     *                      >= 1 and <= 1000.
+     * @throws EvioException block size too small or file cannot be created
+     */
+    public EventWriter(String filename, boolean append) throws EvioException {
+        this(new File(filename), DEFAULT_BLOCK_SIZE, DEFAULT_BLOCK_COUNT,
+             ByteOrder.nativeOrder(), null, null, true, append);
+    }
+
+    /**
+     * Creates an <code>EventWriter</code> for writing to a file in the
+     * specified byte order.
+     * If the file already exists, its contents will be overwritten unless
+     * it is being appended to. If it doesn't exist, it will be created.
+     *
+     * @param filename  name of the file to write to.<br>
+     * @param append    if <code>true</code> and the file already exists,
+     *                  all events to be written will be appended to the
+     *                  end of the file.
      * @param byteOrder the byte order in which to write the file.
+     *
+     * @throws EvioException block size too small or file cannot be created
+     */
+    public EventWriter(String filename, boolean append, ByteOrder byteOrder) throws EvioException {
+        this(new File(filename), DEFAULT_BLOCK_SIZE, DEFAULT_BLOCK_COUNT,
+             byteOrder, null, null, true, append);
+    }
+
+    /**
+     * Create an <code>EventWriter</code> for writing events to a file.
+     * If the file already exists, its contents will be overwritten.
+     * If it doesn't exist, it will be created.
+     *
+     * @param file          the file object to write to.<br>
+     * @param blockSizeMax  the max blocksize to use which must be >= {@link #MIN_BLOCK_SIZE}
+     *                      and <= {@link #MAX_BLOCK_SIZE} ints.
+     *                      The size of the block will not be larger than this size
+     *                      unless a single event itself is larger.
+     * @param blockCountMax the max number of events in a single block which must be
+     *                      >= {@link #MIN_BLOCK_COUNT} and <= {@link #MAX_BLOCK_COUNT}.
+     * @param byteOrder     the byte order in which to write the file.
      * @param xmlDictionary dictionary in xml format or null if none.
-     * @param bitInfo set of bits to include in first block header.
+     * @param bitInfo       set of bits to include in first block header.
+     *
      * @throws EvioException block size too small or file cannot be created
      */
     public EventWriter(File file, int blockSizeMax, int blockCountMax, ByteOrder byteOrder,
-                          String xmlDictionary, BitSet bitInfo) throws EvioException {
+                       String xmlDictionary, BitSet bitInfo)
+                                        throws EvioException {
 
-        if (blockSizeMax < 100 || blockSizeMax > 1000000) {
-            throw new EvioException("Pick a block size >= 1K and <= 1M");
-        }
+        this(file, blockSizeMax, blockCountMax, byteOrder,
+             xmlDictionary, bitInfo, true, false);
+    }
 
-        if (blockCountMax < 1 || blockCountMax > 1000) {
-            throw new EvioException("Pick a block count >= 1 and <= 1000");
-        }
+    /**
+     * Create an <code>EventWriter</code> for writing events to a file.
+     * If the file already exists, its contents will be overwritten
+     * unless the "overWriteOK" argument is <code>false</code> in
+     * which case an exception will be thrown. If it doesn't exist,
+     * it will be created.
+     *
+     * @param file          the file to write to.<br>
+     * @param blockSizeMax  the max blocksize to use which must be >= {@link #MIN_BLOCK_SIZE}
+     *                      and <= {@link #MAX_BLOCK_SIZE} ints.
+     *                      The size of the block will not be larger than this size
+     *                      unless a single event itself is larger.
+     * @param blockCountMax the max number of events in a single block which must be
+     *                      >= {@link #MIN_BLOCK_COUNT} and <= {@link #MAX_BLOCK_COUNT}.
+     * @param byteOrder     the byte order in which to write the file.
+     * @param xmlDictionary dictionary in xml format or null if none.
+     * @param bitInfo       set of bits to include in first block header.
+     * @param overWriteOK   if <code>false</code> and the file already exists,
+     *                      an exception is thrown rather than overwriting it.
+     *
+     * @throws EvioException block size too small, file exists and cannot be deleted,
+     *                       file exists and user requested no deletion.
+     */
+    public EventWriter(File file, int blockSizeMax, int blockCountMax, ByteOrder byteOrder,
+                       String xmlDictionary, BitSet bitInfo, boolean overWriteOK)
+                                        throws EvioException {
 
-    	toFile = true;
-        this.blockSizeMax = blockSizeMax;
-        this.blockCountMax = blockCountMax;
-        this.byteOrder = byteOrder;
-        this.xmlDictionary = xmlDictionary;
+        this(file, blockSizeMax, blockCountMax, byteOrder,
+             xmlDictionary, bitInfo, overWriteOK, false);
+    }
 
-        if (bitInfo != null) {
-            this.bitInfo = (BitSet)bitInfo.clone();
-        }
-        else {
-            this.bitInfo = new BitSet(24);
-        }
 
-        if (xmlDictionary != null) {
-            this.bitInfo.set(0,true);
-        }
-
-		try {
-            fileOutputStream = new FileOutputStream(file);
-		}
-		catch (FileNotFoundException e) {
-			throw new EvioException(e.getMessage());
-		}
-    }
 
     /**
      * Create an <code>EventWriter</code> for writing events to a file.
+     * If the file already exists, its contents will be overwritten
+     * unless the "overWriteOK" argument is <code>false</code> in
+     * which case an exception will be thrown. Unless ..., the option to
+     * append these events to an existing file is <code>true</code>,
+     * in which case everything is fine. If the file doesn't exist,
+     * it will be created. Byte order defaults to big endian if arg is null.
      *
-     * @param file the file to write to. Will be created.<br>
-     *             WARNING: If the file already exists, an attempt will be made to delete it. Appending
-     *             to an existing event file is not supported.
-     * @param blockSizeMax the max blocksize to use which must be >= 1K and <= 1M ints.
-     *                     The size of the block will not be larger than this size.
-     *                     The one exception is when a single event being written is larger
-     *                     than the max size.
+     * @param file          the file to write to.<br>
+     * @param blockSizeMax  the max blocksize to use which must be >= {@link #MIN_BLOCK_SIZE}
+     *                      and <= {@link #MAX_BLOCK_SIZE} ints.
+     *                      The size of the block will not be larger than this size
+     *                      unless a single event itself is larger.
      * @param blockCountMax the max number of events in a single block which must be
-     *                      >= 1 and <= 1000.
-     * @param byteOrder the byte order in which to write the file.
+     *                      >= {@link #MIN_BLOCK_COUNT} and <= {@link #MAX_BLOCK_COUNT}.
+     * @param byteOrder     the byte order in which to write the file. This is ignored
+     *                      if appending to existing file.
      * @param xmlDictionary dictionary in xml format or null if none.
-     * @param bitInfo set of bits to include in first block header.
-     * @param okToDelete if <code>true</code> and the file already exists,
-     *                   an attempt will be made to delete the existing file.
-     *                   Appending to an existing event file is not supported at this time.
-     * @throws EvioException block size too small, file exists and cannot be deleted,
-     *                       file exists and user requested no deletion.
+     * @param bitInfo       set of bits to include in first block header.
+     * @param overWriteOK   if <code>false</code> and the file already exists,
+     *                      an exception is thrown rather than overwriting it.
+     * @param append        if <code>true</code> and the file already exists,
+     *                      all events to be written will be appended to the
+     *                      end of the file.
+     *
+     * @throws EvioException if blockSizeMax or blockCountMax exceed limits;
+     *                       if defined dictionary while appending;
+     *                       if file arg is null;
+     *                       if file could not be opened or positioned;
+     *                       if file exists but user requested no over-writing or appending.
+     *
      */
     public EventWriter(File file, int blockSizeMax, int blockCountMax, ByteOrder byteOrder,
-                          String xmlDictionary, BitSet bitInfo, boolean okToDelete) throws EvioException {
+                          String xmlDictionary, BitSet bitInfo, boolean overWriteOK,
+                          boolean append) throws EvioException {
+
+        if (blockSizeMax < MIN_BLOCK_SIZE || blockSizeMax > MAX_BLOCK_SIZE) {
+            throw new EvioException("Block size arg must be bigger or smaller");
+        }
+
+        if (blockCountMax < MIN_BLOCK_COUNT || blockCountMax > MAX_BLOCK_COUNT) {
+            throw new EvioException("Block count arg must be bigger or smaller");
+        }
 
-        if (blockSizeMax < 100 || blockSizeMax > 1000000) {    // TODO: restore limits
-            throw new EvioException("Pick a block size >= 1K and <= 1M");
+        if (file == null) {
+            throw new EvioException("Null file arg");
         }
 
-        if (blockCountMax < 1 || blockCountMax > 1000) {
-            throw new EvioException("Pick a block count >= 1 and <= 1000");
+        if (byteOrder == null) {
+            byteOrder = ByteOrder.BIG_ENDIAN;
         }
 
-        toFile = true;
-    	this.blockSizeMax = blockSizeMax;
+        toFile             = true;
+        this.append        = append;
+        // byte order may be overwritten if appending
+        this.byteOrder     = byteOrder;
+    	this.blockSizeMax  = blockSizeMax;
         this.blockCountMax = blockCountMax;
-        this.byteOrder = byteOrder;
         this.xmlDictionary = xmlDictionary;
 
+        blockNumber = 1;
+
+        // For convenience, create buffer to hold a last, empty block header.
+        emptyLastHeader = ByteBuffer.allocate(32);
+        emptyLastHeader.order(byteOrder);
+        emptyLastHeader.putInt(8); // block len, words
+        emptyLastHeader.putInt(1); // block number
+        emptyLastHeader.putInt(8); // header len, words
+        emptyLastHeader.putInt(0); // event count
+        emptyLastHeader.putInt(0); // reserved 1
+        emptyLastHeader.putInt(0x204); // last block = true, version = 4
+        emptyLastHeader.putInt(0); // reserved 2
+        emptyLastHeader.putInt(0xc0da0100); // magic #
+        emptyLastHeader.flip();
+
+
         if (bitInfo != null) {
             this.bitInfo = (BitSet)bitInfo.clone();
         }
@@ -309,124 +469,293 @@
         }
 
         if (xmlDictionary != null) {
+            // Appending means not adding new dictionary
+            if (append) {
+                throw new EvioException("Cannot specify dictionary when appending");
+            }
             this.bitInfo.set(0,true);
+            writeDictionary = true;
         }
 
-    	if (file == null) {
-    		throw new EvioException("Null file in EventWriter constructor");
-    	}
-
-    	// handle existing file
-    	if (file.exists() && file.isFile()) {
-    		if (okToDelete) {
-    			boolean deleted = file.delete();
-    			if (!deleted) {
-            		throw new EvioException("File in EventWriter constructor already exists, and could not be deleted. \n" +
-            				"File: " + file.getPath());
-    			}
-    		}
-    		else { // user requested no delete
-        		throw new EvioException("File in EventWriter constructor already exists, and user requested no deletion. \n" +
-        				"File: " + file.getPath());
-    		}
+    	// If we can't overwrite or append and file exists, throw exception
+    	if (!overWriteOK && !append && (file.exists() && file.isFile())) {
+            throw new EvioException("File exists but user requested no over-writing or appending, "
+                                            + file.getPath());
     	}
 
 		try {
-            fileOutputStream = new FileOutputStream(file);
-		}
-		catch (FileNotFoundException e) {
-    		throw new EvioException("For whatever reason, the file could not be opened. \n " +
-    				"It could mean there is no permission to open the file for writing \n" +
-    				"or that the file was a directory." +
-    				"File: " + file.getPath());
+            if (append) {
+                RandomAccessFile raf = new RandomAccessFile(file, "rw");
+                fileChannel = raf.getChannel();
+
+                // Something to read block headers into
+                buffer = ByteBuffer.allocate(32);
+
+                // Look at first block header to find endianness & version.
+                // Endianness given in constructor arg, when appending, is ignored.
+                examineFirstBufferHeader();
+
+                // Oops, gotta redo this since file has different byte order
+                // than specified in constructor arg.
+                if (this.byteOrder != byteOrder) {
+                    emptyLastHeader.clear();
+                    emptyLastHeader.order(this.byteOrder);
+                    emptyLastHeader.putInt(8); // block len, words
+                    emptyLastHeader.putInt(1); // block number
+                    emptyLastHeader.putInt(8); // header len, words
+                    emptyLastHeader.putInt(0); // event count
+                    emptyLastHeader.putInt(0); // reserved 1
+                    emptyLastHeader.putInt(0x204); // last block = true, version = 4
+                    emptyLastHeader.putInt(0); // reserved 2
+                    emptyLastHeader.putInt(0xc0da0100); // magic #
+                    emptyLastHeader.flip();
+                }
+
+                // Prepare for appending by moving file position
+                toAppendPosition();
+
+                // Create block buffer with default size.
+                // This method will be called before each write.
+                // Do this after examineFirstBufferHeader() so the
+                // byte order is set properly. If appending, don't
+                // increase the blockNumber since it was properly
+                // set in toAppendPosition().
+                getCleanBuffer(0,0,false);
+            }
+            else {
+                fileOutputStream = new FileOutputStream(file, append);
+                fileChannel = fileOutputStream.getChannel();
+
+                // Create block buffer w/ default size & set blockNumber to 1
+                getCleanBuffer(0,0,true);
+            }
 		}
+        catch (FileNotFoundException e) {
+            throw new EvioException("File could not be opened for writing, " +
+                                            file.getPath(), e);
+        }
+        catch (IOException e) {
+            throw new EvioException("File could not be positioned for appending, " +
+                                            file.getPath(), e);
+        }
+
     }
 
+
+    //---------------------------------------------
+    // BUFFER Constructors
+    //---------------------------------------------
+
     /**
      * Create an <code>EventWriter</code> for writing events to a ByteBuffer.
+     * Uses the default number and size of blocks in buffer. Will overwrite
+     * any existing data in buffer!
      *
-     * @param buf the buffer to write to.
-     * @param blockSizeMax the max blocksize to use which must be >= 1K and <= 1M ints.
-     *                     The size of the block will not be larger than this size.
-     *                     The one exception is when a single event being written is larger
-     *                     than the max size.
-     * @param blockCountMax the max number of events in a single block which must be
-     *                      >= 1 and <= 1000.
-     * @param xmlDictionary dictionary in xml format or null if none.
-     * @param bitInfo set of bits to include in first block header.
+     * @param buf            the buffer to write to.
+     * @throws EvioException if buf arg is null
+     */
+    public EventWriter(ByteBuffer buf) throws EvioException {
+
+        this(buf, DEFAULT_BLOCK_SIZE, DEFAULT_BLOCK_COUNT, null, null, 0, false);
+    }
+
+
+    /**
+     * Create an <code>EventWriter</code> for writing events to a ByteBuffer.
+     * Uses the default number and size of blocks in buffer.
+     *
+     * @param buf            the buffer to write to.
+     * @param append         if <code>true</code>, all events to be written will be
+     *                       appended to the end of the buffer.
+     * @throws EvioException if buf arg is null
+     */
+    public EventWriter(ByteBuffer buf, boolean append) throws EvioException {
+
+        this(buf, DEFAULT_BLOCK_SIZE, DEFAULT_BLOCK_COUNT, null, null, 0, append);
+    }
+
+
+    /**
+     * Create an <code>EventWriter</code> for writing events to a ByteBuffer.
+     * Uses the default number and size of blocks in buffer.
+     *
+     * @param buf            the buffer to write to.
+     * @param xmlDictionary  dictionary in xml format or null if none.
+     * @param append         if <code>true</code>, all events to be written will be
+     *                       appended to the end of the buffer.
+     * @throws EvioException if buf arg is null
+     */
+    public EventWriter(ByteBuffer buf, String xmlDictionary, boolean append) throws EvioException {
+
+        this(buf, DEFAULT_BLOCK_SIZE, DEFAULT_BLOCK_COUNT, xmlDictionary, null, 0, append);
+    }
+
+
+    /**
+     * Create an <code>EventWriter</code> for writing events to a ByteBuffer.
+     *
+     * @param buf            the buffer to write to.
+     * @param blockSizeMax   the max blocksize to use which must be >= {@link #MIN_BLOCK_SIZE}
+     *                       and <= {@link #MAX_BLOCK_SIZE} ints.
+     *                       The size of the block will not be larger than this size
+     *                       unless a single event itself is larger.
+     * @param blockCountMax  the max number of events in a single block which must be
+     *                       >= {@link #MIN_BLOCK_COUNT} and <= {@link #MAX_BLOCK_COUNT}.
+     * @param xmlDictionary  dictionary in xml format or null if none.
+     * @param bitInfo        set of bits to include in first block header.
      * @throws EvioException if blockSizeMax or blockCountMax exceed limits; if buf arg is null
      */
     public EventWriter(ByteBuffer buf, int blockSizeMax, int blockCountMax,
                        String xmlDictionary, BitSet bitInfo) throws EvioException {
 
-        if (blockSizeMax < 100 || blockSizeMax > 1000000) {  // TODO: restore limits
-            throw new EvioException("Pick a block size >= 1K and <= 1M");
-        }
+        this(buf, blockSizeMax, blockCountMax, xmlDictionary, bitInfo, 0, false);
+    }
 
-        if (blockCountMax < 1 || blockCountMax > 1000) {
-            throw new EvioException("Pick a block count >= 1 and <= 1000");
-        }
 
-        if (buf == null) {
-            throw new EvioException("Buffer arg cannot be null");
-        }
-
-        toFile = false;
-        this.buffer = buf;
-    	this.blockSizeMax = blockSizeMax;
-        this.blockCountMax = blockCountMax;
-        this.xmlDictionary = xmlDictionary;
+    /**
+     * Create an <code>EventWriter</code> for writing events to a ByteBuffer.
+     *
+     * @param buf            the buffer to write to.
+     * @param blockSizeMax   the max blocksize to use which must be >= {@link #MIN_BLOCK_SIZE}
+     *                       and <= {@link #MAX_BLOCK_SIZE} ints.
+     *                       The size of the block will not be larger than this size
+     *                       unless a single event itself is larger.
+     * @param blockCountMax  the max number of events in a single block which must be
+     *                       >= {@link #MIN_BLOCK_COUNT} and <= {@link #MAX_BLOCK_COUNT}.
+     * @param xmlDictionary  dictionary in xml format or null if none.
+     * @param bitInfo        set of bits to include in first block header.
+     * @param append         if <code>true</code>, all events to be written will be
+     *                       appended to the end of the buffer.
+     *
+     * @throws EvioException if blockSizeMax or blockCountMax exceed limits;
+     *                       if buf arg is null;
+     *                       if defined dictionary while appending;
+     */
+    public EventWriter(ByteBuffer buf, int blockSizeMax, int blockCountMax,
+                       String xmlDictionary, BitSet bitInfo,
+                       boolean append) throws EvioException {
 
-        if (bitInfo != null) {
-            this.bitInfo = (BitSet)bitInfo.clone();
-        }
-        else {
-            this.bitInfo = new BitSet(24);
-        }
-        
-        if (xmlDictionary != null) {
-            this.bitInfo.set(0,true);
-        }
+        this(buf, blockSizeMax, blockCountMax, xmlDictionary, bitInfo, 0, append);
     }
 
     /**
      * Create an <code>EventWriter</code> for writing events to a ByteBuffer.
      *
-     * @param buf the buffer to write to.
-     * @param blockSizeMax the max blocksize to use which must be >= 1K and <= 1M ints.
-     *                     The size of the block will not be larger than this size.
-     *                     The one exception is when a single event being written is larger
-     *                     than the max size.
-     * @param blockCountMax the max number of events in a single block which must be
-     *                      >= 1 and <= 1000.
-     * @param xmlDictionary dictionary in xml format or null if none.
-     * @param bitInfo set of bits to include in first block header.
-     * @param reserved1 set the value of the first "reserved" int in first block header.
-     *                  NOTE: only CODA (i.e. EMU) software should use this.
+     * @param buf            the buffer to write to.
+     * @param blockSizeMax   the max blocksize to use which must be >= {@link #MIN_BLOCK_SIZE}
+     *                       and <= {@link #MAX_BLOCK_SIZE} ints.
+     *                       The size of the block will not be larger than this size
+     *                       unless a single event itself is larger.
+     * @param blockCountMax  the max number of events in a single block which must be
+     *                       >= {@link #MIN_BLOCK_COUNT} and <= {@link #MAX_BLOCK_COUNT}.
+     * @param xmlDictionary  dictionary in xml format or null if none.
+     * @param bitInfo        set of bits to include in first block header.
+     * @param reserved1      set the value of the first "reserved" int in first block header.
+     *                       NOTE: only CODA (i.e. EMU) software should use this.
      * @throws EvioException if blockSizeMax or blockCountMax exceed limits; if buf arg is null
      */
     public EventWriter(ByteBuffer buf, int blockSizeMax, int blockCountMax,
                        String xmlDictionary, BitSet bitInfo, int reserved1) throws EvioException {
 
-        if (blockSizeMax < 100 || blockSizeMax > 1000000) {  // TODO: restore limits
-            throw new EvioException("Pick a block size >= 1K and <= 1M");
+        this(buf, blockSizeMax, blockCountMax, xmlDictionary, bitInfo, reserved1, false);
+    }
+
+    /**
+     * Create an <code>EventWriter</code> for writing events to a ByteBuffer.
+     *
+     * @param buf            the buffer to write to.
+     * @param blockSizeMax   the max blocksize to use which must be >= {@link #MIN_BLOCK_SIZE}
+     *                       and <= {@link #MAX_BLOCK_SIZE} ints.
+     *                       The size of the block will not be larger than this size
+     *                       unless a single event itself is larger.
+     * @param blockCountMax  the max number of events in a single block which must be
+     *                       >= {@link #MIN_BLOCK_COUNT} and <= {@link #MAX_BLOCK_COUNT}.
+     * @param xmlDictionary  dictionary in xml format or null if none.
+     * @param bitInfo        set of bits to include in first block header.
+     * @param reserved1      set the value of the first "reserved" int in first block header.
+     *                       NOTE: only CODA (i.e. EMU) software should use this.
+     * @param append         if <code>true</code>, all events to be written will be
+     *                       appended to the end of the buffer.
+     *
+     * @throws EvioException if blockSizeMax or blockCountMax exceed limits;
+     *                       if buf arg is null;
+     *                       if defined dictionary while appending;
+     */
+    private EventWriter(ByteBuffer buf, int blockSizeMax, int blockCountMax,
+                        String xmlDictionary, BitSet bitInfo, int reserved1,
+                        boolean append) throws EvioException {
+
+        initializeBuffer(buf, blockSizeMax, blockCountMax,
+                         xmlDictionary, bitInfo, reserved1, append);
+    }
+
+
+    /**
+     * Encapsulate constructor initialization for buffers.
+     *
+     * @param buf            the buffer to write to.
+     * @param blockSizeMax   the max blocksize to use which must be >= {@link #MIN_BLOCK_SIZE}
+     *                       and <= {@link #MAX_BLOCK_SIZE} ints.
+     *                       The size of the block will not be larger than this size
+     *                       unless a single event itself is larger.
+     * @param blockCountMax  the max number of events in a single block which must be
+     *                       >= {@link #MIN_BLOCK_COUNT} and <= {@link #MAX_BLOCK_COUNT}.
+     * @param xmlDictionary  dictionary in xml format or null if none.
+     * @param bitInfo        set of bits to include in first block header.
+     * @param reserved1      set the value of the first "reserved" int in first block header.
+     *                       NOTE: only CODA (i.e. EMU) software should use this.
+     * @param append         if <code>true</code>, all events to be written will be
+     *                       appended to the end of the buffer.
+     *
+     * @throws EvioException if blockSizeMax or blockCountMax exceed limits;
+     *                       if buf arg is null;
+     *                       if defined dictionary while appending;
+     */
+    private void initializeBuffer(ByteBuffer buf, int blockSizeMax, int blockCountMax,
+                                  String xmlDictionary, BitSet bitInfo, int reserved1,
+                                  boolean append) throws EvioException {
+
+        if (blockSizeMax < MIN_BLOCK_SIZE) {
+            throw new EvioException("Max block size arg (" + blockSizeMax + ") must be >= " +
+                                     MIN_BLOCK_SIZE);
         }
 
-        if (blockCountMax < 1 || blockCountMax > 1000) {
-            throw new EvioException("Pick a block count >= 1 and <= 1000");
+        if (blockSizeMax > MAX_BLOCK_SIZE) {
+            throw new EvioException("Max block size arg (" + blockSizeMax + ") must be <= " +
+                                     MAX_BLOCK_SIZE);
+        }
+
+        if (blockCountMax < MIN_BLOCK_COUNT) {
+            throw new EvioException("Max block count arg (" + blockCountMax + ") must be >= " +
+                                     MIN_BLOCK_COUNT);
+        }
+
+        if (blockCountMax > MAX_BLOCK_COUNT) {
+            throw new EvioException("Max block count arg (" + blockCountMax + ") must be <= " +
+                                     MAX_BLOCK_COUNT);
         }
 
         if (buf == null) {
             throw new EvioException("Buffer arg cannot be null");
         }
 
-        toFile = false;
-        this.buffer = buf;
-        this.reserved1 = reserved1;
-    	this.blockSizeMax = blockSizeMax;
+        this.append        = append;
+        this.buffer        = buf;
+        this.byteOrder     = buf.order();
+        this.reserved1     = reserved1;
+        this.blockSizeMax  = blockSizeMax;
         this.blockCountMax = blockCountMax;
         this.xmlDictionary = xmlDictionary;
 
+        // Init variables
+        toFile = false;
+        closed = false;
+        blockNumber = 1;
+        eventsWritten = 0;
+        holdingListTotalBytes = 0;
+        eventHoldingList.clear();
+        bufferHeaderPosition = initialPosition = buf.position();
+
         if (bitInfo != null) {
             this.bitInfo = (BitSet)bitInfo.clone();
         }
@@ -435,10 +764,59 @@
         }
 
         if (xmlDictionary != null) {
+            // Appending means not adding new dictionary
+            if (append) {
+                throw new EvioException("Cannot specify dictionary when appending");
+            }
             this.bitInfo.set(0,true);
+            writeDictionary = true;
+        }
+
+        // For convenience, create buffer to hold a last, empty block header.
+        emptyLastHeader = ByteBuffer.allocate(32);
+        emptyLastHeader.order(byteOrder);
+        emptyLastHeader.putInt(8); // block len, words
+        emptyLastHeader.putInt(1); // block number
+        emptyLastHeader.putInt(8); // header len, words
+        emptyLastHeader.putInt(0); // event count
+        emptyLastHeader.putInt(0); // reserved 1
+        emptyLastHeader.putInt(0x204); // last block = true, version = 4
+        emptyLastHeader.putInt(0); // reserved 2
+        emptyLastHeader.putInt(0xc0da0100); // magic #
+        emptyLastHeader.flip();
+
+
+        try {
+            if (append) {
+                // Check endianness & version
+                examineFirstBufferHeader();
+
+                // Oops, gotta redo this since buffer
+                // has different byte order than specified.
+                if (byteOrder != buf.order()) {
+                    emptyLastHeader.clear();
+                    emptyLastHeader.order(byteOrder);
+                    emptyLastHeader.putInt(8); // block len, words
+                    emptyLastHeader.putInt(1); // block number
+                    emptyLastHeader.putInt(8); // header len, words
+                    emptyLastHeader.putInt(0); // event count
+                    emptyLastHeader.putInt(0); // reserved 1
+                    emptyLastHeader.putInt(0x204); // last block = true, version = 4
+                    emptyLastHeader.putInt(0); // reserved 2
+                    emptyLastHeader.putInt(0xc0da0100); // magic #
+                    emptyLastHeader.flip();
+                }
+
+                // Prepare for appending by moving buffer position
+                toAppendPosition();
+            }
+        }
[truncated at 1000 lines; 994 more skipped]

jevio-base/src/main/java/org/jlab/coda/jevio
IEvioStructure.java 1.3 -> 1.4
diff -u -r1.3 -r1.4
--- IEvioStructure.java	28 Feb 2012 19:41:36 -0000	1.3
+++ IEvioStructure.java	3 Apr 2013 20:07:46 -0000	1.4
@@ -59,14 +59,6 @@
      */
     public String[] getStringData();
 
-//    /**
-//     * Gets the raw data as a String, if the type as indicated by the
-//     * header is appropriate.
-//     * @return the data as a String, or <code>null</code> if this makes no sense for the given type.
-//     * (The only DataType it makes sense for is CHARSTAR8.)
-//     */
-//    public String getStringData();
-
 	/**
 	 * Gets the raw data as a long array, if the type as indicated by the
 	 * header is appropriate.
@@ -96,14 +88,14 @@
 	public short[] getShortData();
 	
     /**
-     * Gets the raw data as a CompositeData object, if the type as indicated
+     * Gets the raw data as an array of CompositeData objects, if the type as indicated
      * by the header is appropriate.<p>
      *
-     * @return the data as a CompositeData object,
+     * @return the data as an array of CompositeData objects,
      *         or <code>null</code> if this makes no sense for the given type.
      * @throws EvioException if the data is internally inconsistent
      */
-    public CompositeData getCompositeData() throws EvioException;
+    public CompositeData[] getCompositeData() throws EvioException;
 
 	/**
 	 * Get the description from the name provider (dictionary), if there is one.

jevio-base/src/main/java/org/jlab/coda/jevio
EvioEvent.java 1.3 -> 1.4
diff -u -r1.3 -r1.4
--- EvioEvent.java	28 Feb 2012 19:41:36 -0000	1.3
+++ EvioEvent.java	3 Apr 2013 20:07:46 -0000	1.4
@@ -1,8 +1,5 @@
 package org.jlab.coda.jevio;
 
-import java.util.List;
-import java.util.Vector;
-
 import javax.swing.tree.DefaultTreeModel;
 import javax.xml.stream.XMLStreamException;
 import javax.xml.stream.XMLStreamWriter;
@@ -25,6 +22,7 @@
 
 	/**
 	 * As the event is parsed, it is parsed into a tree.
+     * Note that this is only used when parsing.
 	 */
 	protected DefaultTreeModel treeModel;
 	
@@ -38,6 +36,7 @@
      * There may be a dictionary in xml associated with this event. Or there may not.
      */
     private String dictionaryXML;
+    
 
 
     /**
@@ -67,6 +66,46 @@
 		this(new BankHeader(tag, dataType, num));
 	}
 
+    /**
+     * Deep clone of BaseStructure does most of the work.
+     * Leave EvioBank's attachment as a reference. Only
+     * thing we need to do get rid of any cloned treeModel.
+     */
+    public Object clone() {
+        EvioEvent ev = (EvioEvent) super.clone();
+
+        // A treeModel is only created & used if this event was parsed into existence.
+        // To avoid keeping this unused and transient object, just get rid of it.
+        treeModel = null;
+
+        return ev;
+    }
+
+//    /**
+//     * Method to used to help reproduce an EvioEvent's treeModel object.
+//     *
+//     * @param model   Model to add items to
+//     * @param parent  parent TreeNode object
+//     * @param child   child TreeNode object
+//     * @param index   index at which to place child in parent
+//     */
+//    static private void addChildToTree(DefaultTreeModel model, BaseStructure parent,
+//                                BaseStructure child, int index) {
+//        // See if the child has children
+//        Vector<BaseStructure> kids = child.getChildren();
+//
+//        // If not, we're a leaf so add to tree model
+//        if (kids == null || kids.size() < 1) {
+//            model.insertNodeInto(parent, child, index);   // (parent, child, index)
+//            return;
+//        }
+//
+//        // If we have kids, add each to model recursively
+//        int i=0;
+//        for (BaseStructure kid : kids) {
+//            addChildToTree(model, child, kid, i++);
+//        }
+//    }
 
     /**
      * Is there an XML dictionary associated with this event?
@@ -115,15 +154,15 @@
 		return s;
 	}
 	
-	/**
-	 * Get the tree model representing this event. This would have been generated
-	 * as the event was being parsed.
-	 * @return the tree model representing this event.
-	 */
-	public DefaultTreeModel getTreeModel() {
-		return treeModel;
-	}
-	
+    /**
+     * Get the tree model representing this event. This would have been generated
+     * as the event was being parsed.
+     * @return the tree model representing this event.
+     */
+    public DefaultTreeModel getTreeModel() {
+        return treeModel;
+    }
+
 	/**
 	 * Inserts a child structure into the event's JTree. This is called when the event is being parsed,
 	 * and when an event is being created.
@@ -134,83 +173,6 @@
 		treeModel.insertNodeInto(child, parent, parent.getChildCount());
 	}
 	
-	/**
-	 * Visit all the structures in this event (including the event itself--which is considered its own descendant). 
-	 * This is similar to listening to the event as it is being parsed, but is done to a complete (already) parsed event.
-	 * @param listener an listener to notify as each structure is visited.
-	 */
-	public void vistAllStructures(IEvioListener listener) {
-		visitAllDescendants(this, listener, null);
-	}
-
-	/**
-	 * Visit all the structures in this event (including the event itself--which is considered its own descendant) in a depth first manner.
-	 * @param listener an listener to notify as each structure is visited.
-	 * @param filter an optional filter that must "accept" structures before they are passed to the listener. If <code>null</code>, all
-	 * structures are passed. In this way, specific types of structures can be captured.
-	 */
-	public void vistAllStructures(IEvioListener listener, IEvioFilter filter) {
-		visitAllDescendants(this, listener, filter);
-	}
-
-	/**
-	 * Visit all the descendants of a given structure (which is considered a descendant of itself.)
-	 * 
-	 * @param structure the starting structure.
-	 * @param listener an listener to notify as each structure is visited.
-	 * @param filter an optional filter that must "accept" structures before they are passed to the listener. If
-	 *            <code>null</code>, all structures are passed. In this way, specific types of structures can be
-	 *            captured.
-	 */
-	private void visitAllDescendants(BaseStructure structure, IEvioListener listener, IEvioFilter filter) {
-		
-		if (listener != null) {
-			boolean accept = true;
-			if (filter != null) {
-				accept = filter.accept(structure.getStructureType(), structure);
-			}
-			
-			if (accept) {
-			    listener.gotStructure(this, structure);
-			}
-		}
-		
-		if (!(structure.isLeaf())) {
-			for (BaseStructure child : structure.getChildren()) {
-				visitAllDescendants(child, listener, filter);
-			}
-		}
-	}
-	
-	/**
-	 * Visit all the descendant structures, and collect those that pass a filter.
-	 * @param filter the filter that must be passed. If <code>null</code>, this will return all the structures.
-	 * @return a collection of all structures that are accepted by a filter.
-	 */
-	public List<BaseStructure> getMatchingStructures(IEvioFilter filter) {
-		final Vector<BaseStructure> structures = new Vector<BaseStructure>(25, 10);
-		
-		IEvioListener listener = new IEvioListener() {
-            @Override
-            public void startEventParse(EvioEvent evioEvent) { }
-
-            @Override
-            public void endEventParse(EvioEvent evioEvent) { }
-            
-			@Override
-			public void gotStructure(EvioEvent evioEvent, IEvioStructure structure) {
-				structures.add((BaseStructure)structure);
-			}
-		};
-		
-		vistAllStructures(listener, filter);
-		
-		if (structures.size() == 0) {
-			return null;
-		}
-		return structures;
-	}
-	
     /**
      * This returns a number [1..] indicating which event this was in the event file from which
      * it was read. It is not the "num" field from the header.

jevio-base/src/main/java/org/jlab/coda/jevio
ByteDataTransformer.java 1.4 -> 1.5
diff -u -r1.4 -r1.5
--- ByteDataTransformer.java	20 Mar 2012 23:21:49 -0000	1.4
+++ ByteDataTransformer.java	3 Apr 2013 20:07:46 -0000	1.5
@@ -4,8 +4,6 @@
 import java.io.DataOutputStream;
 import java.io.IOException;
 import java.nio.*;
-import java.util.ArrayList;
-import java.util.List;
 
 /**
  * This utility class contains methods for transforming a raw byte array into arrays of
@@ -256,10 +254,15 @@
      * @param data short array to convert
      * @param byteOrder byte order of returned bytes (big endian if null)
      * @return byte array representing short array or null if data is null
+     * @throws EvioException if data array has too many elements to convert to a byte array
      */
-    public static byte[] toBytes(short[] data, ByteOrder byteOrder) {
+    public static byte[] toBytes(short[] data, ByteOrder byteOrder) throws EvioException {
 
         if (data == null) return null;
+        if (data.length > Integer.MAX_VALUE/2) {
+            throw new EvioException("short array has too many elements to convert to byte array");
+        }
+
         if (byteOrder == null) {
             byteOrder = ByteOrder.BIG_ENDIAN;
         }
@@ -282,15 +285,20 @@
       * @param byteOrder byte order of written bytes (big endian if null)
       * @param dest array in which to store written bytes
       * @param off offset into dest array where returned bytes are placed
-      * @throws EvioException if data is null, dest is null or too small, or offset negative
+      * @throws EvioException if data is null, dest is null or too small, or offset negative;
+      *                       if data array has too many elements to convert to a byte array
       */
      public static void toBytes(short[] data, ByteOrder byteOrder, byte[] dest, int off)
-             throws EvioException{
+             throws EvioException {
 
          if (data == null || dest == null || dest.length < 2*data.length+off || off < 0) {
              throw new EvioException("bad arg(s)");
          }
 
+         if (data.length > Integer.MAX_VALUE/2) {
+             throw new EvioException("short array has too many elements to convert to byte array");
+         }
+
          if (byteOrder == null) {
              byteOrder = ByteOrder.BIG_ENDIAN;
          }
@@ -371,10 +379,16 @@
      * @param data int array to convert
      * @param byteOrder byte order of returned bytes (big endian if null)
      * @return byte array representing int array or null if data is null
+     * @throws EvioException if data array as too many elements to
+     *                       convert to a byte array
      */
-    public static byte[] toBytes(int[] data, ByteOrder byteOrder) {
+    public static byte[] toBytes(int[] data, ByteOrder byteOrder) throws EvioException {
 
         if (data == null) return null;
+        if (data.length > Integer.MAX_VALUE/4) {
+            throw new EvioException("int array has too many elements to convert to byte array");
+        }
+
         if (byteOrder == null) {
             byteOrder = ByteOrder.BIG_ENDIAN;
         }
@@ -397,11 +411,18 @@
      * @param length number of integers to conver to bytes
      * @param byteOrder byte order of returned bytes (big endian if null)
      * @return byte array representing int array or null if data is null
+     * @throws EvioException if data array has too many elements to
+     *                       convert to a byte array
      */
-    public static byte[] toBytes(int[] data, int offset, int length, ByteOrder byteOrder) {
+    public static byte[] toBytes(int[] data, int offset, int length, ByteOrder byteOrder)
+                    throws EvioException {
 
         if (data == null) return null;
         if (offset < 0 || length < 1) return null;
+        if (data.length > Integer.MAX_VALUE/4) {
+            throw new EvioException("int array has too many elements to convert to byte array");
+        }
+
         if (byteOrder == null) {
             byteOrder = ByteOrder.BIG_ENDIAN;
         }
@@ -424,7 +445,8 @@
      * @param byteOrder byte order of written bytes (big endian if null)
      * @param dest array in which to store written bytes
      * @param off offset into dest array where returned bytes are placed
-     * @throws EvioException if data is null, dest is null or too small, or offset negative
+     * @throws EvioException if data is null, dest is null or too small, or offset negative;
+     *                       if data array has too many elements to convert to a byte array
      */
     public static void toBytes(int[] data, ByteOrder byteOrder, byte[] dest, int off)
             throws EvioException{
@@ -433,6 +455,10 @@
             throw new EvioException("bad arg(s)");
         }
 
+        if (data.length > Integer.MAX_VALUE/4) {
+            throw new EvioException("int array has too many elements to convert to byte array");
+        }
+
         if (byteOrder == null) {
             byteOrder = ByteOrder.BIG_ENDIAN;
         }
@@ -454,10 +480,15 @@
      * @param data int array to convert
      * @param byteOrder byte order of returned bytes (big endian if null)
      * @return byte array representing int array
+     * @throws EvioException if data array has too many elements to
+     *                       convert to a byte array
      */
-    public static byte[] toBytesStream(int[] data, ByteOrder byteOrder) {
+    public static byte[] toBytesStream(int[] data, ByteOrder byteOrder) throws EvioException {
 
         if (data == null) return null;
+        if (data.length > Integer.MAX_VALUE/4) {
+            throw new EvioException("int array has too many elements to convert to byte array");
+        }
 
         ByteArrayOutputStream baos = new ByteArrayOutputStream(4*data.length);
         DataOutputStream out = new DataOutputStream(baos);
@@ -564,10 +595,16 @@
      * @param data long array to convert
      * @param byteOrder byte order of returned bytes (big endian if null)
      * @return byte array representing long array or null if data is null
+     * @throws EvioException if data array has too many elements to
+     *                       convert to a byte array
      */
-    public static byte[] toBytes(long[] data, ByteOrder byteOrder) {
+    public static byte[] toBytes(long[] data, ByteOrder byteOrder) throws EvioException {
 
         if (data == null) return null;
+        if (data.length > Integer.MAX_VALUE/8) {
+            throw new EvioException("long array has too many elements to convert to byte array");
+        }
+
         if (byteOrder == null) {
             byteOrder = ByteOrder.BIG_ENDIAN;
         }
@@ -590,7 +627,8 @@
      * @param byteOrder byte order of written bytes (big endian if null)
      * @param dest array in which to store written bytes
      * @param off offset into dest array where returned bytes are placed
-     * @throws EvioException if data is null, dest is null or too small, or offset negative
+     * @throws EvioException if data is null, dest is null or too small, or offset negative;
+     *                       if data array has too many elements to convert to a byte array
      */
     public static void toBytes(long[] data, ByteOrder byteOrder, byte[] dest, int off)
             throws EvioException{
@@ -599,6 +637,10 @@
             throw new EvioException("bad arg(s)");
         }
 
+        if (data.length > Integer.MAX_VALUE/8) {
+            throw new EvioException("long array has too many elements to convert to byte array");
+        }
+
         if (byteOrder == null) {
             byteOrder = ByteOrder.BIG_ENDIAN;
         }
@@ -647,10 +689,16 @@
      * @param data float array to convert
      * @param byteOrder byte order of returned bytes (big endian if null)
      * @return byte array representing float array or null if data is null
+     * @throws EvioException if data array has too many elements to
+     *                       convert to a byte array
      */
-    public static byte[] toBytes(float[] data, ByteOrder byteOrder) {
+    public static byte[] toBytes(float[] data, ByteOrder byteOrder) throws EvioException {
 
         if (data == null) return null;
+        if (data.length > Integer.MAX_VALUE/4) {
+            throw new EvioException("float array has too many elements to convert to byte array");
+        }
+
         if (byteOrder == null) {
             byteOrder = ByteOrder.BIG_ENDIAN;
         }
@@ -672,7 +720,8 @@
      * @param byteOrder byte order of written bytes (big endian if null)
      * @param dest array in which to store written bytes
      * @param off offset into dest array where returned bytes are placed
-     * @throws EvioException if data is null, dest is null or too small, or offset negative
+     * @throws EvioException if data is null, dest is null or too small, or offset negative;
+     *                       if data array has too many elements to convert to a byte array
      */
     public static void toBytes(float[] data, ByteOrder byteOrder, byte[] dest, int off)
             throws EvioException{
@@ -681,6 +730,10 @@
             throw new EvioException("bad arg(s)");
         }
 
+        if (data.length > Integer.MAX_VALUE/4) {
+            throw new EvioException("float array has too many elements to convert to byte array");
+        }
+
         if (byteOrder == null) {
             byteOrder = ByteOrder.BIG_ENDIAN;
         }
@@ -728,10 +781,16 @@
      * @param data double array to convert
      * @param byteOrder byte order of returned bytes (big endian if null)
      * @return byte array representing double array or null if data is null
+     * @throws EvioException if data array has too many elements to
+     *                       convert to a byte array
      */
-    public static byte[] toBytes(double[] data, ByteOrder byteOrder) {
+    public static byte[] toBytes(double[] data, ByteOrder byteOrder) throws EvioException {
 
         if (data == null) return null;
+        if (data.length > Integer.MAX_VALUE/8) {
+            throw new EvioException("double array has too many elements to convert to byte array");
+        }
+
         if (byteOrder == null) {
             byteOrder = ByteOrder.BIG_ENDIAN;
         }
@@ -753,7 +812,8 @@
      * @param byteOrder byte order of written bytes (big endian if null)
      * @param dest array in which to store written bytes
      * @param off offset into dest array where returned bytes are placed
-     * @throws EvioException if data is null, dest is null or too small, or offset negative
+     * @throws EvioException if data is null, dest is null or too small, or offset negative;
+     *                       if data array has too many elements to convert to a byte array
      */
     public static void toBytes(double[] data, ByteOrder byteOrder, byte[] dest, int off)
             throws EvioException{
@@ -762,6 +822,10 @@
             throw new EvioException("bad arg(s)");
         }
 
+        if (data.length > Integer.MAX_VALUE/8) {
+            throw new EvioException("double array has too many elements to convert to byte array");
+        }
+
         if (byteOrder == null) {
             byteOrder = ByteOrder.BIG_ENDIAN;
         }
@@ -784,10 +848,15 @@
      * @param data double array to convert
      * @param byteOrder byte order of returned bytes (big endian if null)
      * @return byte array representing double array or null if data is null
+     * @throws EvioException if data array has too many elements to
+     *                       convert to a byte array
      */
-    public static byte[] toBytes2(double[] data, ByteOrder byteOrder) {
+    public static byte[] toBytes2(double[] data, ByteOrder byteOrder) throws EvioException {
 
         if (data == null) return null;
+        if (data.length > Integer.MAX_VALUE/8) {
+            throw new EvioException("double array has too many elements to convert to byte array");
+        }
 
         byte[] stor = new byte[8];
         byte[] byts = new byte[data.length*8];
@@ -815,7 +884,8 @@
      * @param byteOrder byte order of returned bytes (big endian if null)
      * @param dest array in which to store returned bytes
      * @param off offset into dest array where returned bytes are placed
-     * @throws EvioException if data is null, dest is null or too small, or offset negative
+     * @throws EvioException if data is null, dest is null or too small, or offset negative;
+     *                       if data array has too many elements to convert to a byte array
      */
     public static void toBytes2(double[] data, ByteOrder byteOrder, byte[] dest, int off)
             throws EvioException{
@@ -823,6 +893,9 @@
         if (data == null || dest == null || dest.length < 8*data.length+off || off < 0) {
             throw new EvioException("bad arg(s)");
         }
+        if (data.length > Integer.MAX_VALUE/8) {
+            throw new EvioException("double array has too many elements to convert to byte array");
+        }
 
         byte[] stor = new byte[8];
 

jevio-base/src/main/java/org/jlab/coda/jevio
IBlockHeader.java 1.1 -> 1.2
diff -u -r1.1 -r1.2
--- IBlockHeader.java	28 Feb 2012 19:41:36 -0000	1.1
+++ IBlockHeader.java	3 Apr 2013 20:07:46 -0000	1.2
@@ -96,11 +96,12 @@
     /**
 	 * Gives the bytes remaining in this block (physical record) given a buffer position. The position is an absolute
      * position in a byte buffer. This assumes that the absolute position in <code>bufferStartingPosition</code> is
-     * being maintained properly by the reader.
+     * being maintained properly by the reader. No block is longer than 2.1GB - 31 bits of length. This is for
+     * practical reasons - so a block can be read into a single byte array.
      *
      * @param position the absolute current position is a byte buffer.
      * @return the number of bytes remaining in this block (physical record.)
-     * @throws org.jlab.coda.jevio.EvioException
+     * @throws EvioException
      */
     int bytesRemaining(int position) throws EvioException;
 

jevio-base/src/main/java/org/jlab/coda/jevio
DataType.java 1.4 -> 1.5
diff -u -r1.4 -r1.5
--- DataType.java	20 Mar 2012 23:21:49 -0000	1.4
+++ DataType.java	3 Apr 2013 20:07:46 -0000	1.5
@@ -1,6 +1,5 @@
 package org.jlab.coda.jevio;
 
-import org.omg.CORBA.NamedValue;
 
 /**
  * This an enum used to convert data type numerical values to a more meaningful name. For example, the data type with

jevio-base/src/main/java/org/jlab/coda/jevio
IEvioListener.java 1.3 -> 1.4
diff -u -r1.3 -r1.4
--- IEvioListener.java	28 Feb 2012 19:41:36 -0000	1.3
+++ IEvioListener.java	3 Apr 2013 20:07:46 -0000	1.4
@@ -13,23 +13,23 @@
 	/**
 	 * Called when a structure is read while parsing an event.
 	 * 
-	 * NOTE: the user should NOT modify the event or the structure.
+	 * NOTE: the user should NOT modify the arguments.
 	 * 
-	 * @param evioEvent the data type, BANK, SEGMENT, or TAGSEGMENT
+	 * @param topStructure the evio structure at the top of the search/parse
 	 * @param structure the full structure, including header
 	 */
-	public void gotStructure(EvioEvent evioEvent, IEvioStructure structure);
+	public void gotStructure(BaseStructure topStructure, IEvioStructure structure);
     
     /**
-     * Starting to parse a new event.
-     * @param evioEvent the event in question.
+     * Starting to parse a new evio structure.
+     * @param structure the evio structure in question.
      */
-    public void startEventParse(EvioEvent evioEvent);
+    public void startEventParse(BaseStructure structure);
 
     /**
-     * Done parsing a new event.
-     * @param evioEvent the event in question.
+     * Done parsing a new evio structure.
+     * @param structure the evio structure in question.
      */
-    public void endEventParse(EvioEvent evioEvent);
+    public void endEventParse(BaseStructure structure);
 
 }

jevio-base/src/main/java/org/jlab/coda/jevio
EvioReader.java 1.2 -> 1.3
diff -u -r1.2 -r1.3
--- EvioReader.java	20 Mar 2012 23:21:49 -0000	1.2
+++ EvioReader.java	3 Apr 2013 20:07:46 -0000	1.3
@@ -3,6 +3,7 @@
 import java.io.*;
 import java.nio.*;
 import java.nio.channels.FileChannel;
+import java.util.ArrayList;
 
 import javax.xml.stream.FactoryConfigurationError;
 import javax.xml.stream.XMLOutputFactory;
@@ -10,13 +11,13 @@
 import javax.xml.stream.XMLStreamWriter;
 
 /**
- * This is the a class of interest to the user. It is used to read an evio version 4 or earlier
+ * This is a class of interest to the user. It is used to read an evio version 4 or earlier
  * format file or buffer. Create an <code>EvioReader</code> from a <code>File</code>
  * object corresponding to an event file, and from this class you can test the file
- * for consistency and, more importantly, you can call <code>parseNextEvent</code>
- * to get new events and to stream the embedded structures to an IEvioListener.
- * The same can be done by creating an <code>EvioReader</code> from a
- * <code>ByteBuffer</code>.
+ * for consistency and, more importantly, you can call {@link #parseNextEvent} or
+ * {@link #parseEvent(int)} to get new events and to stream the embedded structures
+ * to an IEvioListener. The same can be done by creating an <code>EvioReader</code>
+ * from a <code>ByteBuffer</code>.
  *
  * The streaming effect of parsing an event is that the parser will read the event and hand off structures,
  * such as banks, to any IEvioListeners. For those familiar with XML, the event is processed SAX-like.
@@ -25,9 +26,6 @@
  * As an alternative to stream processing, after an event is parsed, the user can use the events treeModel
  * for access to the structures. For those familiar with XML, the event is processed DOM-like.
  * <p>
- * NOTE: Even though this class has a constructor that accepts an i/o mode, that is for backwards
- * compatibility only. An <code>EvioReader</code> is used for reading and parsing events only.
- * To write an event file or buffer, use an <code>EventWriter</code> object.
  *
  * @author heddle
  * @author timmer
@@ -72,6 +70,10 @@
     private static final String ROOT_ELEMENT = "evio-data";
 
 
+    /** Skip through a file/buffer and store positions of all the events
+     *  in random access mode for versions 4+. */
+    private final ArrayList<Integer> eventPositions = new ArrayList<Integer>(10000);
+
 
     /** Used to assign a transient number [1..n] to events as they are being read. */
     private int eventNumber = 0;
@@ -104,9 +106,6 @@
     /** Block number expected when reading. Used to check sequence of blocks. */
     private int blockNumberExpected = 1;
 
-    /** Difference between expected & actual block numbers when reading. For warning messages. */
-    private int blockNumberDiff;
-
     /** If true, throw an exception if block numbers are out of sequence. */
     private boolean checkBlockNumberSequence;
 
@@ -142,8 +141,65 @@
     private MappedByteBuffer mappedByteBuffer;
 
 
+    //------------------------
+    // EvioReader's state
+    //------------------------
+
+    /**
+     * This class stores the state of this reader so it can be recovered
+     * after a state-changing method has been called -- like {@link #rewind()}.
+     */
+    private class ReaderState {
+        private boolean lastBlock;
+        private int eventNumber;
+        private int byteBufferPosition;
+        private int blockNumberExpected;
+        private BlockHeaderV2 blockHeader2;
+        private BlockHeaderV4 blockHeader4;
+    }
+
+    /**
+     * This method saves the current state of this EvioReader object.
+     * @return the current state of this EvioReader object.
+     */
+    private ReaderState getState() {
+        ReaderState currentState = new ReaderState();
+        currentState.lastBlock   = lastBlock;
+        currentState.eventNumber = eventNumber;
+        currentState.byteBufferPosition  = byteBuffer.position();
+        currentState.blockNumberExpected = blockNumberExpected;
+        if (evioVersion > 3) {
+            currentState.blockHeader4 = (BlockHeaderV4)blockHeader4.clone();
+        }
+        else {
+            currentState.blockHeader2 = (BlockHeaderV2)blockHeader2.clone();
+        }
+
+        return currentState;
+    }
+
+
+    /**
+     * This method restores a previously saved state of this EvioReader object.
+     * @param state a previously stored state of this EvioReader object.
+     */
+    private void restoreState(ReaderState state) {
+        lastBlock = state.lastBlock;
+        eventNumber = state.eventNumber;
+        byteBuffer.position(state.byteBufferPosition);
+
+        blockNumberExpected = state.blockNumberExpected;
+        if (evioVersion > 3) {
+            blockHeader = blockHeader4 = state.blockHeader4;
+        }
+        else {
+            blockHeader = blockHeader2 = state.blockHeader2;
+        }
+    }
 
 
+    //------------------------
+
     private void printBuffer(ByteBuffer buf, int lenInInts) {
         IntBuffer ibuf = buf.asIntBuffer();
         for (int i=0; i < lenInInts; i++) {
@@ -157,70 +213,174 @@
      * @param path the full path to the file that contains events.
      *             For writing event files, use an <code>EventWriter</code> object.
      * @see EventWriter
-     * @throws IOException if read failure
+     * @throws IOException   if read failure
+     * @throws EvioException if file arg is null
      */
-    public EvioReader(String path) throws IOException {
+    public EvioReader(String path) throws EvioException, IOException {
         this(new File(path));
     }
 
     /**
      * Constructor for reading an event file.
      *
+     * @param path the full path to the file that contains events.
+     *             For writing event files, use an <code>EventWriter</code> object.
+     * @param checkBlkNumSeq if <code>true</code> check the block number sequence
+     *                       and throw an exception if it is not sequential starting
+     *                       with 1
+     * @see EventWriter
+     * @throws IOException   if read failure
+     * @throws EvioException if file arg is null;
+     *                       if first block number != 1 when checkBlkNumSeq arg is true
+     */
+    public EvioReader(String path, boolean checkBlkNumSeq) throws EvioException, IOException {
+        this(new File(path), checkBlkNumSeq);
+    }
+
+    /**
+     * Constructor for reading an event file.
+     *
      * @param file the file that contains events.
-     * @throws IOException if read failure
+     * @see EventWriter
+     * @throws IOException   if read failure
+     * @throws EvioException if file arg is null
+     */
+    public EvioReader(File file) throws EvioException, IOException {
+        this(file, false);
+    }
+
+
+    /**
+     * Constructor for reading an event file.
+     *
+     * @param file the file that contains events.
+     * @param checkBlkNumSeq if <code>true</code> check the block number sequence
+     *                       and throw an exception if it is not sequential starting
+     *                       with 1
+     * @see EventWriter
+     * @throws IOException   if read failure
+     * @throws EvioException if file arg is null;
+     *                       if first block number != 1 when checkBlkNumSeq arg is true
      */
-    public EvioReader(File file) throws IOException {
+    public EvioReader(File file, boolean checkBlkNumSeq)
+                                        throws EvioException, IOException {
+        if (file == null) {
+            throw new EvioException("File arg is null");
+        }
+
+        checkBlockNumberSequence = checkBlkNumSeq;
         initialPosition = 0;
 
         FileInputStream fileInputStream = new FileInputStream(file);
         path = file.getAbsolutePath();
-        FileChannel inputChannel = fileInputStream.getChannel();
-        mapFile(inputChannel);
-        inputChannel.close(); // this object is no longer needed
 
-        // Read first event in case it's a dictionary, we'll also
-        // find the file's endianness & evio version #.
-        try {
-            nextEvent();
-        }
-        catch (EvioException e) {
-            throw new IOException("Failed reading first event", e);
+        FileChannel fileChannel = fileInputStream.getChannel();
+        mapFile(fileChannel);
+        fileChannel.close(); // this object is no longer needed
+
+        // Read first block header and find the file's endianness & evio version #.
+        // If there's a dictionary, read that too.
+        if (getFirstHeader() != ReadStatus.SUCCESS) {
+            throw new IOException("Failed reading first block header/dictionary");
         }
 
-        // reset buffer to beginning
-        rewind();
+        // For the lastest evio format, generate a table
+        // of all event positions in buffer for random access.
+        if (evioVersion > 3) {
+            eventCount = 0;
+            generateEventPositionTable();
+        }
 
         parser = new EventParser();
     }
 
+    /**
+     * Constructor for reading a buffer.
+     *
+     * @param byteBuffer the buffer that contains events.
+     * @see EventWriter
+     * @throws IOException   if read failure
+     * @throws EvioException if file arg is null
+     */
+    public EvioReader(ByteBuffer byteBuffer) throws EvioException, IOException {
+        this(byteBuffer, false);
+    }
 
     /**
      * Constructor for reading a buffer.
-     * TODO: Perhaps a method to reset the buffer would be more efficient than creating a new object.
      *
      * @param byteBuffer the buffer that contains events.
-     * @throws IOException if read failure
+     * @param checkBlkNumSeq if <code>true</code> check the block number sequence
+     *                       and throw an exception if it is not sequential starting
+     *                       with 1
+     * @see EventWriter
+     * @throws IOException   if read failure
+     * @throws EvioException if file arg is null;
+     *                       if first block number != 1 when checkBlkNumSeq arg is true
      */
-    public EvioReader(ByteBuffer byteBuffer) throws IOException {
+    public EvioReader(ByteBuffer byteBuffer, boolean checkBlkNumSeq)
+                                                    throws EvioException, IOException {
+
+        if (byteBuffer == null) {
+            throw new EvioException("Buffer arg is null");
+        }
+
+        checkBlockNumberSequence = checkBlkNumSeq;
         initialPosition = byteBuffer.position();
         this.byteBuffer = byteBuffer;
 
-        // Read first event in case it's a dictionary, we'll also
-        // find the file's endianness & evio version #.
-        try {
-            nextEvent();
-        }
-        catch (EvioException e) {
-            throw new IOException("Failed reading first event", e);
+        // Read first block header and find the file's endianness & evio version #.
+        // If there's a dictionary, read that too.
+        if (getFirstHeader() != ReadStatus.SUCCESS) {
+            throw new IOException("Failed reading first block header/dictionary");
         }
 
-        // reset buffer to beginning
-        rewind();
+        // For the lastest evio format, generate a table
+        // of all event positions in buffer for random access.
+        if (evioVersion > 3) {
+            eventCount = 0;
+            generateEventPositionTable();
+        }
 
         parser = new EventParser();
     }
 
     /**
+     * This method can be used to avoid creating additional EvioReader
+     * objects by reusing this one with another buffer. The method
+     * {@link #close()} is called before anything else.
+     *
+     * @param buf ByteBuffer to be read
+     * @throws IOException   if read failure
+     * @throws EvioException if first block number != 1 when checkBlkNumSeq arg is true
+     */
+    public synchronized void setBuffer(ByteBuffer buf) throws EvioException, IOException {
+
+        close();
+
+        lastBlock           = false;
+        eventNumber         = 0;
+        eventCount          = -1;
+        blockNumberExpected = 1;
+        dictionaryXML       = null;
+        initialPosition     = buf.position();
+        this.byteBuffer     = buf;
+
+        // Read first block header and find the file's endianness & evio version #.
+        // If there's a dictionary, read that too.
+        if (getFirstHeader() != ReadStatus.SUCCESS) {
+            throw new IOException("Failed reading first block header/dictionary");
+        }
+
+        // For the lastest evio format, generate a table
+        // of all event positions in buffer for random access.
+        if (evioVersion > 3) {
+            eventCount = 0;
+            generateEventPositionTable();
+        }
+    }
+
+    /**
      * Is this reader checking the block number sequence and
      * throwing an exception is it's not sequential and starting with 1?
      * @return <code>true</code> if checking block number sequence, else <code>false</code>
@@ -230,25 +390,6 @@
     }
 
     /**
-     * Set whether this reader will check the block number sequence
-     * and throw an exception is it's not sequential and starting with 1.
-     * This method must be called before parsing events if you want to
-     * change from not checking to checking.
-     * @param checkBlockNumberSequence <code>true</code> to check block number sequence,
-     *                                 else <code>false</code>
-     */
-    public void checkBlockNumberSequence(boolean checkBlockNumberSequence) {
-         // Are we at the top of the file?
-         // If not, do NOT start checking block
-         // numbers part way into reading events.
-         if (byteBuffer.position() != 0 && checkBlockNumberSequence) {
-             return;
-         }
-
-        this.checkBlockNumberSequence = checkBlockNumberSequence;
-    }
-
-    /**
      * Get the evio version number.
      * @return evio version number.
      */
@@ -352,112 +493,180 @@
     }
 
     /**
-     * Reads the block (physical record) header. Assumes the mapped buffer is positioned
-     * at the start of the next block header (physical record.)<br>
+     * Generate a table (ArrayList) of positions of events in file/buffer.
+     * This method does <b>not</b> affect the byteBuffer position, eventNumber,
+     * or lastBlock values. Valid only in versions 4 and later.
      *
-     * A Bank header is 8, 32-bit ints. The first int is the size of the block in ints
-     * (not counting the length itself, i.e., the number of ints to follow).
-     *
-     * Most users should have no need for this method, since most applications do not
-     * care about the block (physical record) header.
-     * 
-     * @return status of read attempt
+     * @throws EvioException if version 3 or earlier
      */
-    protected synchronized ReadStatus nextBlockHeader() {
+    private void generateEventPositionTable() throws EvioException {
 
-        // we already read the last block header
-        if (lastBlock) {
-            return ReadStatus.END_OF_FILE;
+        if (evioVersion < 4) {
+            throw new EvioException("Unsupported version (" + evioVersion + ")");
         }
 
-        // have enough remaining?
+        int      i, position, byteLen, bytesLeft, blockEventCount, blockHdrSize;
+        boolean  curLastBlock=false, firstBlock=true, hasDictionary=false;
+
+
+        // Start at the beginning of byteBuffer without changing
+        // its current position. Do this with absolute gets.
+        position  = initialPosition;
+        bytesLeft = byteBuffer.limit() - position;
+//        System.out.println("generatePositionTable: ");
+//        System.out.println("    byte buf cap  = " + byteBuffer.capacity());
+//        System.out.println("    byte buf lim  = " + byteBuffer.limit());
+//        System.out.println("    byte buf pos  = " + byteBuffer.position());
+//        System.out.println("    bytesLeft     = " + bytesLeft);
+//        System.out.println("    (initial) pos = " + position);
+//        System.out.println();
+
+        while (!curLastBlock) {
+//System.out.println("generatePositionTable: pos = " + position +
+//                           ", ver pos = " + (position + 4*BlockHeaderV4.EV_VERSION) +
+//                           ", h siz pos = " + (position + 4*BlockHeaderV4.EV_HEADERSIZE) +
+//                           ", ev count pos = " + (position + 4*BlockHeaderV4.EV_COUNT));
+
+            // Look at block header to get info. Swapping is taken care of.
+            i               = byteBuffer.getInt(position + 4*BlockHeaderV4.EV_VERSION);
+            blockHdrSize    = byteBuffer.getInt(position + 4*BlockHeaderV4.EV_HEADERSIZE);
+            blockEventCount = byteBuffer.getInt(position + 4*BlockHeaderV4.EV_COUNT);
+
+            eventCount  += blockEventCount;
+            curLastBlock = BlockHeaderV4.isLastBlock(i);
+            if (firstBlock) hasDictionary = BlockHeaderV4.hasDictionary(i);
+
+//            System.out.println("    ver       = 0x" + Integer.toHexString(i));
+//            System.out.println("    hdr size  = 0x" + Integer.toHexString(blockHdrSize));
+//            System.out.println("    blk count = 0x" + Integer.toHexString(blockEventCount));
+//            System.out.println("    last blk  = " + curLastBlock);
+//            System.out.println("    has dict  = " + hasDictionary);
+//            System.out.println();
+
+            // Hop over block header to data
+            position  += 4*blockHdrSize;
+            bytesLeft -= 4*blockHdrSize;
+
+//System.out.println("    hopped blk hdr, bytesLeft = " + bytesLeft + ", pos = " + position);
+            // Check for a dictionary - the first event in the first block.
+            // It's not included in the header block count, but we must take
+            // it into account by skipping over it.
+            if (firstBlock && hasDictionary) {
+                firstBlock = false;
+
+                // Get its length - bank's len does not include itself
+                byteLen = 4*(byteBuffer.getInt(position) + 1);
+
+                // Skip over dictionary
+                position  += byteLen;
+                bytesLeft -= byteLen;
+//System.out.println("    hopped dict, bytesLeft = " + bytesLeft + ", pos = " + position);
+            }
+
+            // For each event in block, store its location
+            for (i=0; i < blockEventCount; i++) {
+                // Sanity check - must have at least 1 header's amount left
+                if (bytesLeft < 8) {
+                    throw new EvioException("File/buffer bad format");
+                }
+
+                // Store current position
+                eventPositions.add(position);
+
+                // Get length of current event (including full header)
+                byteLen = 4*(byteBuffer.getInt(position) + 1);
+
+                position  += byteLen;
+                bytesLeft -= byteLen;
+//System.out.println("    hopped event, bytesLeft = " + bytesLeft + ", pos = " + position + "\n");
+            }
+        }
+
+    }
+
+
+    /**
+     * Reads part of the first block (physical record) header in order to determine
+     * the evio version # and endianness of the file or buffer in question. These things
+     * do <b>not</b> need to be examined in subsequent block headers.
+     *
+     * @return status of read attempt
+     */
+    protected synchronized ReadStatus getFirstHeader() {
+
+        // Have enough remaining bytes to read?
         if (byteBuffer.remaining() < 32) {
             byteBuffer.clear();
             return ReadStatus.END_OF_FILE;
         }
 
         try {
-//            System.out.println("Print header =\n");
-//            printBuffer(byteBuffer, 8);
-            // Set the byte order to match the file's ordering.
+            // Set the byte order to match the buffer/file's ordering.
 
             // Check the magic number for endianness. This requires
             // peeking ahead 7 ints or 28 bytes. Set the endianness
             // once we figure out what it is (buffer defaults to big endian).
             byteOrder = byteBuffer.order();
             int magicNumber = byteBuffer.getInt(MAGIC_OFFSET);
-//System.out.println("nextBlockHeader: actual magic number = " + Integer.toHexString(BlockHeaderV4.MAGIC_NUMBER));
-//System.out.println("nextBlockHeader: read magic number as " + Integer.toHexString(magicNumber));
 
             if (magicNumber != IBlockHeader.MAGIC_NUMBER) {
-                // Originally checked ByteOrder.nativeOrder() which is NOT what you want -  timmer
                 if (byteOrder == ByteOrder.BIG_ENDIAN) {
                     byteOrder = ByteOrder.LITTLE_ENDIAN;
-//System.out.println("nextBlockHeader: reset byte order to " + ByteOrder.LITTLE_ENDIAN);
                 }
                 else {
                     byteOrder = ByteOrder.BIG_ENDIAN;
-//System.out.println("nextBlockHeader: reset byte order to " + ByteOrder.BIG_ENDIAN);
                 }
                 byteBuffer.order(byteOrder);
 
-                // reread magic number to make sure things are OK
+                // Reread magic number to make sure things are OK
                 magicNumber = byteBuffer.getInt(MAGIC_OFFSET);
                 if (magicNumber != IBlockHeader.MAGIC_NUMBER) {
 System.out.println("ERROR reread magic # (" + magicNumber + ") & still not right");
                     return ReadStatus.EVIO_EXCEPTION;
                 }
             }
-            else {
-//System.out.println("nextBlockHeader: byte order = " + byteOrder);
-            }
-
 
             // Check the version number. This requires peeking ahead 5 ints or 20 bytes.
-            // Use the correct class for the block header once we know the version.
-            int version = byteBuffer.getInt(VERSION_OFFSET) & VERSION_MASK;
-//System.out.println("nextBlockHeader: version = " + version);
-
-            // TODO: wrap ByteBuffer with IntBuffer for faster reading
+            evioVersion = byteBuffer.getInt(VERSION_OFFSET) & VERSION_MASK;
+            if (evioVersion < 1)  {
+                return ReadStatus.EVIO_EXCEPTION;
+            }
+//System.out.println("Evio version# = " + evioVersion);
 
             try {
-                if (version > 0 && version < 4) {
-                    // cache the starting position
-                    blockHeader2.setBufferStartingPosition(byteBuffer.position());
-
-                    // read the header data.
-                    blockHeader2.setSize(byteBuffer.getInt());
-                    blockHeader2.setNumber(byteBuffer.getInt());
-                    blockHeader2.setHeaderLength(byteBuffer.getInt());
-                    blockHeader2.setStart(byteBuffer.getInt());
-                    blockHeader2.setEnd(byteBuffer.getInt());
-                    // skip version
-                    byteBuffer.getInt();
-                    blockHeader2.setVersion(version);
-                    blockHeader2.setReserved1(byteBuffer.getInt());
-                    blockHeader2.setMagicNumber(byteBuffer.getInt());
-                    blockHeader = blockHeader2;
-                }
-                else if (version >= 4) {
-                    // cache the starting position
+                if (evioVersion >= 4) {
+                    // Cache the starting position
                     blockHeader4.setBufferStartingPosition(byteBuffer.position());
-
-                    // read the header data.
+                    // Read the header data.
                     blockHeader4.setSize(byteBuffer.getInt());
                     blockHeader4.setNumber(byteBuffer.getInt());
                     blockHeader4.setHeaderLength(byteBuffer.getInt());
                     blockHeader4.setEventCount(byteBuffer.getInt());
-                    // unused
-                    byteBuffer.getInt();
-                    // use 6th word to set bit info
+                    blockHeader4.setReserved1(byteBuffer.getInt());
+
+                    // Use 6th word to set bit info & version
                     blockHeader4.parseToBitInfo(byteBuffer.getInt());
-                    blockHeader4.setVersion(version);
+                    blockHeader4.setVersion(evioVersion);
                     lastBlock = blockHeader4.getBitInfo(1);
-                    // unused
-                    byteBuffer.getInt();
+
+                    blockHeader4.setReserved2(byteBuffer.getInt());
                     blockHeader4.setMagicNumber(byteBuffer.getInt());
                     blockHeader = blockHeader4;
 
+                    // Deal with non-standard header lengths here
+                    int headerLenDiff = blockHeader4.getHeaderLength() - BlockHeaderV4.HEADER_SIZE;
+                    // If too small quit with error since headers have a minimum size
+                    if (headerLenDiff < 0) {
+                        return ReadStatus.EVIO_EXCEPTION;
+                    }
+                    // If bigger, read extra ints
+                    else if (headerLenDiff > 0) {
+                        for (int i=0; i < headerLenDiff; i++) {
+//System.out.println("Getting extra header int");
+                            byteBuffer.getInt();
+                        }
+                    }
+
 //System.out.println("BlockHeader v4:");
 //System.out.println("   block length  = " + blockHeader4.getSize() + " ints");
 //System.out.println("   block number  = " + blockHeader4.getNumber());
@@ -469,35 +678,38 @@
 //System.out.println("   magic number  = " + Integer.toHexString(blockHeader4.getMagicNumber()));
 //System.out.println();
 
+                    // Is there a dictionary? If so, read it here.
+                    if (blockHeader4.hasDictionary()) {
+                        readDictionary();
+                    }
                 }
                 else {
-                    // error
-System.err.println("ERROR unsupported evio version number in file or buffer");
-                    return ReadStatus.EVIO_EXCEPTION;
-                }
+                    // cache the starting position
+                    blockHeader2.setBufferStartingPosition(byteBuffer.position());
 
-                // each file is restricted to one version, so set it once
-                if (evioVersion < 1) {
-                    evioVersion = version;
+                    // read the header data.
+                    blockHeader2.setSize(byteBuffer.getInt());
+                    blockHeader2.setNumber(byteBuffer.getInt());
+                    blockHeader2.setHeaderLength(byteBuffer.getInt());
+                    blockHeader2.setStart(byteBuffer.getInt());
+                    blockHeader2.setEnd(byteBuffer.getInt());
+                    // skip version
+                    byteBuffer.getInt();
+                    blockHeader2.setVersion(evioVersion);
+                    blockHeader2.setReserved1(byteBuffer.getInt());
+                    blockHeader2.setMagicNumber(byteBuffer.getInt());
+                    blockHeader = blockHeader2;
                 }
 
                 // check block number if so configured
                 if (checkBlockNumberSequence) {
                     if (blockHeader.getNumber() != blockNumberExpected) {
-System.out.println("block number out of sequence, got " +
-                    blockHeader.getNumber() + " expecting " + blockNumberExpected);
-                        blockNumberExpected++;
+
+System.out.println("block # out of sequence, got " + blockHeader.getNumber() +
+                           " expecting " + blockNumberExpected);
+
                         return ReadStatus.EVIO_EXCEPTION;
                     }
-                }
-                // otherwise print out a warning if number skips
-                else {
-                    if (blockHeader.getNumber() != blockNumberExpected + blockNumberDiff) {
-                        System.out.println("block number out of sequence, got " +
-                                            blockHeader.getNumber() + " expecting " +
-                                            (blockNumberExpected + blockNumberDiff));
-                    }
-                    blockNumberDiff = blockHeader.getNumber() - blockNumberExpected;
                     blockNumberExpected++;
                 }
             }
@@ -505,6 +717,7 @@
                 e.printStackTrace();
                 return ReadStatus.EVIO_EXCEPTION;
             }
+
         }
         catch (BufferUnderflowException a) {
 System.err.println("ERROR endOfBuffer " + a);
@@ -517,19 +730,318 @@
 
 
     /**
-      * Get the next event in the file. As useful as this sounds, most applications will probably call
-      * {@link #parseNextEvent() parseNextEvent} instead, since it combines combines getting the next
-      * event with parsing the next event.<p>
-      * In evio version 4, events no longer cross block boundaries. There are only one or more
-      * complete events in each block. No changes were made to this method from versions 1-3 in order
-      * to read the version 4 format as it is subset of versions 1-3 with variable block length.
-      *
-      * @return the next event in the file. On error it throws an EvioException.
-      *         On end of file, it returns <code>null</code>.
-      * @throws EvioException if failed read due to bad buffer format
-      */
-     public synchronized EvioEvent nextEvent() throws EvioException {
-         EvioEvent event = new EvioEvent();
+     * Reads the block (physical record) header. Assumes the mapped buffer is positioned
+     * at the start of the next block header (physical record.) By the time this is called,
+     * the version # and byte order have already been determined. Not necessary to do that
+     * for each block header that's read.<br>
+     *
+     * A Bank header is 8, 32-bit ints. The first int is the size of the block in ints
+     * (not counting the length itself, i.e., the number of ints to follow).
+     *
+     * Most users should have no need for this method, since most applications do not
+     * care about the block (physical record) header.
+     *
+     * @return status of read attempt
+     */
+    protected synchronized ReadStatus nextBlockHeader() {
+
+        // We already read the last block header
+        if (lastBlock) {
+            return ReadStatus.END_OF_FILE;
+        }
+
+        // Have enough remaining?
+        if (byteBuffer.remaining() < 32) {
+            byteBuffer.clear();
+            return ReadStatus.END_OF_FILE;
+        }
+
+        try {
+            if (evioVersion >= 4) {
+                // Cache the starting position
+                blockHeader4.setBufferStartingPosition(byteBuffer.position());
+
+                // Read the header data.
+                blockHeader4.setSize(byteBuffer.getInt());
+                blockHeader4.setNumber(byteBuffer.getInt());
+                blockHeader4.setHeaderLength(byteBuffer.getInt());
+                blockHeader4.setEventCount(byteBuffer.getInt());
+                blockHeader4.setReserved1(byteBuffer.getInt());
+                // Use 6th word to set bit info
+                blockHeader4.parseToBitInfo(byteBuffer.getInt());
+                blockHeader4.setVersion(evioVersion);
+                lastBlock = blockHeader4.getBitInfo(1);
+                blockHeader4.setReserved2(byteBuffer.getInt());
+                blockHeader4.setMagicNumber(byteBuffer.getInt());
+                blockHeader = blockHeader4;
+
+                // Deal with non-standard header lengths here
+                int headerLenDiff = blockHeader4.getHeaderLength() - BlockHeaderV4.HEADER_SIZE;
+                // If too small quit with error since headers have a minimum size
+                if (headerLenDiff < 0) {
+                    return ReadStatus.EVIO_EXCEPTION;
+                }
+                // If bigger, read extra ints
+                else if (headerLenDiff > 0) {
+                    for (int i=0; i < headerLenDiff; i++) {
+                        byteBuffer.getInt();
+                    }
+                }
+
+//System.out.println("BlockHeader v4:");
+//System.out.println("   block length  = " + blockHeader4.getSize() + " ints");
+//System.out.println("   block number  = " + blockHeader4.getNumber());
+//System.out.println("   header length = " + blockHeader4.getHeaderLength() + " ints");
+//System.out.println("   event count   = " + blockHeader4.getEventCount());
+//System.out.println("   version       = " + blockHeader4.getVersion());
+//System.out.println("   has Dict      = " + blockHeader4.getBitInfo(0));
+//System.out.println("   is End        = " + lastBlock);
+//System.out.println("   magic number  = " + Integer.toHexString(blockHeader4.getMagicNumber()));
+//System.out.println();
+
+            }
+            else if (evioVersion < 4) {
+                // cache the starting position
+                blockHeader2.setBufferStartingPosition(byteBuffer.position());
+
+                // read the header data.
+                blockHeader2.setSize(byteBuffer.getInt());
+                blockHeader2.setNumber(byteBuffer.getInt());
+                blockHeader2.setHeaderLength(byteBuffer.getInt());
+                blockHeader2.setStart(byteBuffer.getInt());
+                blockHeader2.setEnd(byteBuffer.getInt());
+                // skip version
+                byteBuffer.getInt();
+                blockHeader2.setVersion(evioVersion);
+                blockHeader2.setReserved1(byteBuffer.getInt());
+                blockHeader2.setMagicNumber(byteBuffer.getInt());
+                blockHeader = blockHeader2;
+            }
+            else {
+                // bad version # - should never happen
+                return ReadStatus.EVIO_EXCEPTION;
+            }
+
+            // check block number if so configured
+            if (checkBlockNumberSequence) {
+                if (blockHeader.getNumber() != blockNumberExpected) {
+
+System.out.println("block # out of sequence, got " + blockHeader.getNumber() +
+                   " expecting " + blockNumberExpected);
+
+                    return ReadStatus.EVIO_EXCEPTION;
+                }
+                blockNumberExpected++;
+            }
+        }
+        catch (EvioException e) {
+            e.printStackTrace();
+            return ReadStatus.EVIO_EXCEPTION;
+        }
+        catch (BufferUnderflowException a) {
+System.err.println("ERROR endOfBuffer " + a);
+            byteBuffer.clear();
+            return ReadStatus.UNKNOWN_ERROR;
+        }
+
+        return ReadStatus.SUCCESS;
+    }
+
+
+    /**
+     * This method is only called once at the very beginning if buffer is known to have
+     * a dictionary. It then reads that dictionary. Only called in versions 4 & up.
+     *
+     * @return <code>true</code> if dictionary was found & read, else <code>false</code>.
+     * @throws EvioException if failed read due to bad buffer format;
+     *                       if version 3 or earlier
+     */
+     private synchronized void readDictionary() throws EvioException {
+
+         if (evioVersion < 4) {
+             throw new EvioException("Unsupported version (" + evioVersion + ")");
+         }
+
+         // How many bytes remain in this block?
+         int bytesRemaining = blockBytesRemaining();
+         if (bytesRemaining < 12) {
+             throw new EvioException("Not enough data in first block");
+         }
+
+         // Once here, we are assured the entire next event is in this block.
+         int length = byteBuffer.getInt();
+         if (length < 1) {
+             throw new EvioException("Bad value for dictionary length (too big for java?)");
+         }
+         bytesRemaining -= 4;
+
+         // Since we're only interested in length, read but ignore rest of the header.
+         byteBuffer.getInt();
+         bytesRemaining -= 4;
+
+         // get the raw data
+         int eventDataSizeBytes = 4*(length - 1);
+         if (bytesRemaining < eventDataSizeBytes) {
+             throw new EvioException("Not enough data in first block");
+         }
+
+         byte bytes[] = new byte[eventDataSizeBytes];
+
+         // Read in dictionary data
+         try {
+             byteBuffer.get(bytes, 0, eventDataSizeBytes);
+         }
+         catch (Exception e) {
+             throw new EvioException("Problems reading buffer");
+         }
+
+         // This is the very first event and must be a dictionary
+         String[] strs = BaseStructure.unpackRawBytesToStrings(bytes, 0);
+         if (strs == null) {
+             throw new EvioException("Data in bad format");
+         }
+         dictionaryXML = strs[0];
+     }
+
+
+    /**
+     * Get the event in the file/buffer at a given index (starting at 1).
+     * As useful as this sounds, most applications will probably call
+     * {@link #parseEvent(int)} instead, since it combines combines
+     * getting the event with parsing it.<p>
+     *
+     * @param  index number of event desired, starting at 1, from beginning of file/buffer
+     * @return the event in the file at the given index or null if none
+     * @throws EvioException if failed read due to bad file/buffer format;
+     *                       if out of memory; if index < 1
+     */
+    public synchronized EvioEvent getEvent(int index) throws EvioException {
+
+        if (index < 1) {
+            throw new EvioException("index arg starts at 1");
+        }
+
+        if (index > eventPositions.size()) {
+            return null;
+        }
+
+        if (evioVersion < 4) {
+            return gotoEventNumber(index);
+        }
+
+        index--;
+
+        EvioEvent event = new EvioEvent();
+        BaseStructureHeader header = event.getHeader();
+
+        int eventDataSizeBytes = 0;
+        // Save the current position
+        int origPosition = byteBuffer.position();
+        // Go to the correct position for the desired event
+        int position = eventPositions.get(index);
+        byteBuffer.position(position);
+
+        // Read length of event
+        int length = byteBuffer.getInt();
+        if (length < 1) {
+            throw new EvioException("Bad file/buffer format");
+        }
+        header.setLength(length);
+
+        try {
+            // Read second header word
+            if (byteOrder == ByteOrder.BIG_ENDIAN) {
+                // Interested in bit pattern, not negative numbers
+                header.setTag(ByteDataTransformer.shortBitsToInt(byteBuffer.getShort()));
+                int dt = ByteDataTransformer.byteBitsToInt(byteBuffer.get());
+                int type = dt & 0x3f;
+                int padding = dt >>> 6;
+                // If only 7th bit set, that can only be the legacy tagsegment type
+                // with no padding information - convert it properly.
+                if (dt == 0x40) {
+                    type = DataType.TAGSEGMENT.getValue();
+                    padding = 0;
+                }
+                header.setDataType(type);
+                header.setPadding(padding);
+
+                // Once we know what the data type is, let the no-arg constructed
+                // event know what type it is holding so xml names are set correctly.
+                event.setXmlNames();
+                header.setNumber(ByteDataTransformer.byteBitsToInt(byteBuffer.get()));
+            }
+            else {
+                header.setNumber(ByteDataTransformer.byteBitsToInt(byteBuffer.get()));
+                int dt = ByteDataTransformer.byteBitsToInt(byteBuffer.get());
+                int type = dt & 0x3f;
+                int padding = dt >>> 6;
+                if (dt == 0x40) {
+                    type = DataType.TAGSEGMENT.getValue();
+                    padding = 0;
+                }
+                header.setDataType(type);
+                header.setPadding(padding);
+
+                event.setXmlNames();
+                header.setTag(ByteDataTransformer.shortBitsToInt(byteBuffer.getShort()));
+            }
+
+            // Read the raw data
+            eventDataSizeBytes = 4*(header.getLength() - 1);
+            byte bytes[] = new byte[eventDataSizeBytes];
+            byteBuffer.get(bytes, 0, eventDataSizeBytes);
+
+            event.setRawBytes(bytes);
+            event.setByteOrder(byteOrder);
+            event.setEventNumber(index + 1);
+
+        }
+        catch (OutOfMemoryError e) {
+            throw new EvioException("Out Of Memory: (event size = " + eventDataSizeBytes + ")", e);
+        }
+        catch (Exception e) {
+            throw new EvioException("Error", e);
+        }
+        finally {
+            // Reset the buffer position in all cases
+            byteBuffer.position(origPosition);
+        }
+
+        return event;
+    }
+
+
+	/**
+	 * This is the workhorse method. It retrieves the desired event from the file/buffer,
+     * and then parses it SAX-like. It will drill down and uncover all structures
+     * (banks, segments, and tagsegments) and notify any interested listeners.
+	 *
+     * @param  index number of event desired, starting at 1, from beginning of file/buffer
+	 * @return the parsed event at the given index or null if none
+     * @throws EvioException if failed read due to bad file/buffer format;
+     *                       if out of memory; if index < 1
+	 */
+	public synchronized EvioEvent parseEvent(int index) throws EvioException {
+		EvioEvent event = getEvent(index);
+        if (event != null) parseEvent(event);
+		return event;
+	}
+
+
+    /**
+     * Get the next event in the file. As useful as this sounds, most applications will probably call
+     * {@link #parseNextEvent()} instead, since it combines combines getting the next
+     * event with parsing the next event.<p>
+     * In evio version 4, events no longer cross block boundaries. There are only one or more
+     * complete events in each block. No changes were made to this method from versions 1-3 in order
+     * to read the version 4 format as it is subset of versions 1-3 with variable block length.
+     *
+     * @return the next event in the file. On error it throws an EvioException.
+     *         On end of file, it returns <code>null</code>.
+     * @throws EvioException if failed read due to bad buffer format
+     */
+    public synchronized EvioEvent nextEvent() throws EvioException {
+        EvioEvent event = new EvioEvent();
          BaseStructureHeader header = event.getHeader();
 
          // Are we at the top of the file?
@@ -540,7 +1052,6 @@
                  throw new EvioException("Failed reading block header in nextEvent.");
              }
              isFirstEvent = true;
-//System.out.println("nextEvent: BLOCK HEADER :\n" + blockHeader.toString());
          }
[truncated at 1000 lines; 213 more skipped]

jevio-base/src/main/java/org/jlab/coda/jevio
EvioFileTest.java 1.4 -> 1.5
diff -u -r1.4 -r1.5
--- EvioFileTest.java	20 Mar 2012 23:21:49 -0000	1.4
+++ EvioFileTest.java	3 Apr 2013 20:07:46 -0000	1.5
@@ -34,7 +34,7 @@
 		while (status == ReadStatus.SUCCESS) {
 			status = reader.nextBlockHeader();
 			if (status == ReadStatus.SUCCESS) {
-				reader.position(reader.getCurrentBlockHeader().nextBufferStartingPosition());
+				reader.position((int)reader.getCurrentBlockHeader().nextBufferStartingPosition());
 				count++;
 			}
 		}
@@ -60,7 +60,7 @@
 		while (status == ReadStatus.SUCCESS) {
 			status = reader.nextBlockHeader();
 			if (status == ReadStatus.SUCCESS) {
-				reader.position(reader.getCurrentBlockHeader().nextBufferStartingPosition());
+				reader.position((int)reader.getCurrentBlockHeader().nextBufferStartingPosition());
 				blockCount++;
 			}
 		}

jevio-base/src/main/java/org/jlab/coda/jevio
StructureFinder.java 1.3 -> 1.4
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>&nbsp;getMatchingStructures(IEvioFilter)</code>
+ * within an event, bank, segment, or tagsegment that match certain criteria. For the most
+ * part it uses the <code>List<BaseStructure>&nbsp;getMatchingStructures(IEvioFilter)</code>
  * method on the provided <code>EvioEvent</code> object by constructing the
  * appropriate filter.
  *
@@ -17,26 +19,27 @@
 	
 	/**
 	 * Collect all the structures in an event that pass a filter.
-	 * @param event the event being queried.
+	 * @param structure the event/bank/seg/tagseg being queried.
 	 * @param filter the filter that must be passed. If <code>null</code>, this will return all the structures.
 	 * @return a collection of all structures that are accepted by a filter for the provided event.
 	 */
-	public static List<BaseStructure> getMatchingStructures(EvioEvent event, IEvioFilter filter) {
-		if (event == null) {
+	public static List<BaseStructure> getMatchingStructures(BaseStructure structure, IEvioFilter filter) {
+		if (structure == null) {
+System.out.println("getMatchingStructures: returning null list");
 			return null;
 		}
-		return event.getMatchingStructures(filter);
+		return structure.getMatchingStructures(filter);
 	}
 	
 	/**
 	 * Collect all the banks in an event that match a provided tag and number in their header.
      * Only Banks are returned, because only Banks have a number field.
-	 * @param event the event being queried.
+     * @param structure the event/bank/seg/tagseg being queried.
 	 * @param tag the tag to match.
 	 * @param number the number to match.
 	 * @return a collection of all Banks that are accepted by a filter for the provided event.
 	 */
-	public static List<BaseStructure> getMatchingBanks(EvioEvent event, final int tag, final int number) {
+	public static List<BaseStructure> getMatchingBanks(BaseStructure structure, final int tag, final int number) {
 		IEvioFilter filter = new IEvioFilter() {
 			@Override
 			public boolean accept(StructureType type, IEvioStructure struct) {
@@ -45,51 +48,53 @@
                        (number == struct.getHeader().number);
 			}
 		};
-		return getMatchingStructures(event, filter);
+		return getMatchingStructures(structure, filter);
 	}	
 	
 	/**
 	 * Collect all the structures in an event that match a provided tag in their header.
-	 * @param event the event being queried.
+     * @param structure the event/bank/seg/tagseg being queried.
 	 * @param tag the tag to match.
 	 * @return a collection of all structures that are accepted by a filter for the provided event.
 	 */
-	public static List<BaseStructure> getMatchingStructures(EvioEvent event, final int tag) {
+	public static List<BaseStructure> getMatchingStructures(BaseStructure structure, final int tag) {
 		IEvioFilter filter = new IEvioFilter() {
 			@Override
             public boolean accept(StructureType type, IEvioStructure struct) {
 				return (tag == struct.getHeader().tag);
 			}
 		};
-		return getMatchingStructures(event, filter);
+		return getMatchingStructures(structure, filter);
 	}	
 
 	/**
 	 * Collect all the non-banks (i.e., Segments and TagSegments) in an event that match
      * a provided tag in their header. No Banks are returned.
-	 * @param event the event being queried.
+     * @param structure the event/bank/seg/tagseg being queried.
 	 * @param tag the tag to match.
 	 * @return a collection of all non-bank structures that are accepted by a filter for the provided event.
 	 */
-	public static List<BaseStructure> getMatchingNonBanks(EvioEvent event, final int tag) {
+	public static List<BaseStructure> getMatchingNonBanks(BaseStructure structure, final int tag) {
 		IEvioFilter filter = new IEvioFilter() {
 			@Override
             public boolean accept(StructureType type, IEvioStructure struct) {
 				return (type != StructureType.BANK) && (tag == struct.getHeader().tag);
 			}
 		};
-		return getMatchingStructures(event, filter);
+		return getMatchingStructures(structure, filter);
 	}	
 
+
     /**
-     * Collect all structures in an event that match the given dictionary description.
-     * @param event the event being queried.
-     * @param description dictionary description of structures to be returned.
+     * Collect all structures in an event that match the given dictionary name.
+     *
+     * @param structure the event/bank/seg/tagseg being queried.
+     * @param name       dictionary name of structures to be returned.
      * @param dictionary dictionary to be used; if null, an existing global dictionary will be used.
-     * @return a list of BaseStructures that have the given description
+     * @return a list of BaseStructures that have the given name
      * @throws EvioException if no dictionary is defined
      */
-    public static List<BaseStructure> getMatchingStructures(EvioEvent event, String description,
+    public static List<BaseStructure> getMatchingStructures(BaseStructure structure, String name,
                                                             INameProvider dictionary)
             throws EvioException {
 
@@ -104,34 +109,166 @@
             }
         }
 
-        // This IEvioFilter selects structures that match the given dictionary description.
+        // This IEvioFilter selects structures that match the given dictionary name
         class myEvioFilter implements IEvioFilter {
-            String description;
+            String name;
             INameProvider dictionary;
             boolean useGlobalDictionary;
 
-            myEvioFilter(boolean useGlobalDictionary, String description, INameProvider dictionary) {
-                this.description = description;
-                this.dictionary  = dictionary;
+            myEvioFilter(boolean useGlobalDictionary, String name, INameProvider dictionary) {
+                this.name = name;
+                this.dictionary = dictionary;
                 this.useGlobalDictionary = useGlobalDictionary;
             }
 
-            public boolean accept(StructureType structureType, IEvioStructure structure) {
-                String desc;
+            public boolean accept(StructureType structureType, IEvioStructure struct) {
+                String dictName;
                 if (useGlobalDictionary) {
-                    desc = NameProvider.getName((BaseStructure)structure);
+                    dictName = NameProvider.getName((BaseStructure)struct);
                 }
                 else {
-                    desc = dictionary.getName((BaseStructure)structure);
+                    dictName = dictionary.getName((BaseStructure)struct);
                 }
 
-                // if this child matches the description add it to the list
-                return description.equals(desc);
+                // If this structure matches the name, add it to the list
+                return name.equals(dictName);
             }
         };
 
-        myEvioFilter filter = new myEvioFilter(useGlobalDictionary, description, dictionary);
-        return getMatchingStructures(event, filter);
+        myEvioFilter filter = new myEvioFilter(useGlobalDictionary, name, dictionary);
+        return getMatchingStructures(structure, filter);
     }
 
+
+    /**
+     * Collect all structures in an event whose <b>parent</b> has the given dictionary name.
+     *
+     * @param structure  the event/bank/seg/tagseg being queried.
+     * @param parentName dictionary name of parent of structures to be returned.
+     * @param dictionary dictionary to be used; if null, an existing global dictionary will be used.
+     * @return a list of BaseStructures whose parent has the given name
+     * @throws EvioException if no dictionary is defined
+     */
+    public static List<BaseStructure> getMatchingParent(BaseStructure structure, String parentName,
+                                                        INameProvider dictionary)
+            throws EvioException {
+
+        boolean useGlobalDictionary = false;
+
+        if (dictionary == null) {
+            if (!NameProvider.isProviderSet())  {
+                throw new EvioException("Dictionary must be given as arg or defined globally in NameProvider");
+            }
+            else {
+                useGlobalDictionary = true;
+            }
+        }
+
+        // This IEvioFilter selects structures whose parent has the given dictionary name
+        class myEvioFilter implements IEvioFilter {
+            String name;
+            INameProvider dictionary;
+            boolean useGlobalDictionary;
+
+            myEvioFilter(boolean useGlobalDictionary, String name, INameProvider dictionary) {
+                this.name = name;
+                this.dictionary = dictionary;
+                this.useGlobalDictionary = useGlobalDictionary;
+            }
+
+            public boolean accept(StructureType structureType, IEvioStructure struct) {
+                String dictName;
+
+                BaseStructure parent = ((BaseStructure)struct).getParent();
+                if (parent == null) {
+                    return false;
+                }
+
+                if (useGlobalDictionary) {
+                    dictName = NameProvider.getName(parent);
+                }
+                else {
+                    dictName = dictionary.getName(parent);
+                }
+
+                // If this parent matches the name, add it to the list
+                return name.equals(dictName);
+            }
+        };
+
+        myEvioFilter filter = new myEvioFilter(useGlobalDictionary, parentName, dictionary);
+        return getMatchingStructures(structure, filter);
+    }
+
+
+    /**
+     * Collect all structures in an event who has a <b>child</b> with the given dictionary name.
+     *
+     * @param structure  the event/bank/seg/tagseg being queried.
+     * @param childName  dictionary name of a child of structures to be returned.
+     * @param dictionary dictionary to be used; if null, an existing global dictionary will be used.
+     * @return a list of BaseStructures who has a child with the given name
+     * @throws EvioException if no dictionary is defined
+     */
+    public static List<BaseStructure> getMatchingChild(BaseStructure structure, String childName,
+                                                       INameProvider dictionary)
+            throws EvioException {
+
+        boolean useGlobalDictionary = false;
+
+        if (dictionary == null) {
+            if (!NameProvider.isProviderSet())  {
+                throw new EvioException("Dictionary must be given as arg or defined globally in NameProvider");
+            }
+            else {
+                useGlobalDictionary = true;
+            }
+        }
+
+        // This IEvioFilter selects structures who have a child with the given dictionary name
+        class myEvioFilter implements IEvioFilter {
+            String name;
+            INameProvider dictionary;
+            boolean useGlobalDictionary;
+
+            myEvioFilter(boolean useGlobalDictionary, String name, INameProvider dictionary) {
+                this.name = name;
+                this.dictionary = dictionary;
+                this.useGlobalDictionary = useGlobalDictionary;
+            }
+
+            public boolean accept(StructureType structureType, IEvioStructure struct) {
+                String dictName;
+
+                Vector<BaseStructure> children = ((BaseStructure)struct).getChildren();
+                if (children == null || children.size() < 1) {
+                    return false;
+                }
+
+                BaseStructure bStruct;
+                Enumeration<BaseStructure> enumeration = children.elements();
+                while (enumeration.hasMoreElements()) {
+                    bStruct = enumeration.nextElement();
+                    if (useGlobalDictionary) {
+                        dictName = NameProvider.getName(bStruct);
+                    }
+                    else {
+                        dictName = dictionary.getName(bStruct);
+                    }
+
+                    if (name.equals(dictName)) {
+                        // If this child matches the name, add it to the list
+                        return true;
+                    }
+                }
+
+                return false;
+            }
+        };
+
+        myEvioFilter filter = new myEvioFilter(useGlobalDictionary, childName, dictionary);
+        return getMatchingStructures(structure, filter);
+    }
+
+
 }

jevio-base/src/main/java/org/jlab/coda/jevio
EvioFile.java 1.4 -> 1.5
diff -u -r1.4 -r1.5
--- EvioFile.java	8 Mar 2012 20:39:26 -0000	1.4
+++ EvioFile.java	3 Apr 2013 20:07:46 -0000	1.5
@@ -792,7 +792,7 @@
                     }
                     // note: getEventCount() does not include dictionary
                     eventCount += blockHeader4.getEventCount();
-                    mappedByteBuffer.position(blockHeader4.nextBufferStartingPosition());
+                    mappedByteBuffer.position((int)blockHeader4.nextBufferStartingPosition());
                 }
             }
             else {

jevio-base/src/main/java/org/jlab/coda/jevio
EventParser.java 1.4 -> 1.5
diff -u -r1.4 -r1.5
--- EventParser.java	20 Mar 2012 23:21:49 -0000	1.4
+++ EventParser.java	3 Apr 2013 20:07:46 -0000	1.5
@@ -84,8 +84,6 @@
 
 		// if it is not a structure of structures, we are done. That will leave the raw bytes in
 		// the "leaf" structures (e.g., a bank of ints) --which will be interpreted by the various get data methods.
-//System.out.println("Structure = \n" + structure.toString());
-//System.out.println("Header = \n" + structure.getHeader());
 		DataType dataType = structure.getHeader().getDataType();
 		if (!dataType.isStructure()) {
 			// notify the listeners
@@ -188,7 +186,7 @@
         }
         // notify the listeners
 		notifyEvioListeners(evioEvent, structure);
-	} // parseStructure
+	}
 
     /**
      * Create a bank header from the first eight bytes of the data array.

jevio-base/src/main/java/org/jlab/coda/jevio
Demo.java 1.4 -> 1.5
diff -u -r1.4 -r1.5
--- Demo.java	20 Mar 2012 23:21:49 -0000	1.4
+++ Demo.java	3 Apr 2013 20:07:46 -0000	1.5
@@ -45,13 +45,12 @@
         String testEventFile = cwd + File.separator + "testdata" + File.separator + "out.ev";
         String testDictFile  = cwd + File.separator + "testdata" + File.separator + "eviodict.xml";
 
-		EvioReader reader = null;
 
 		try {
 			File file = new File(testEventFile);
 			System.out.println("ev file: " + testEventFile + " size: " + file.length());
 
-			reader = new EvioReader(testEventFile);
+			EvioReader reader = new EvioReader(testEventFile);
 			
 			// uncomment below to test a filter
 //			IEvioFilter myFilter = new IEvioFilter() {
@@ -87,16 +86,16 @@
 			e.printStackTrace();
 			System.exit(1);
 		}
-		catch (IOException e) {
+		catch (Exception e) {
 			e.printStackTrace();
 			System.exit(1);
 		}
 
 	}
 
-    public void startEventParse(EvioEvent evioEvent) { }
+    public void startEventParse(BaseStructure evioEvent) { }
 
-    public void endEventParse(EvioEvent evioEvent) { }
+    public void endEventParse(BaseStructure evioEvent) { }
 
 	/**
 	 * This IEvioListener has received a structure as the result of an event being parsed.
@@ -105,7 +104,7 @@
 	 * or TAGSEGMENT.
 	 */
 	@Override
-	public void gotStructure(EvioEvent evioEvent, IEvioStructure structure) {
+	public void gotStructure(BaseStructure evioEvent, IEvioStructure structure) {
 
 		BaseStructureHeader header = structure.getHeader();
 

jevio-base/src/main/java/org/jlab/coda/jevio
EvioXMLDictionary.java 1.3 -> 1.4
diff -u -r1.3 -r1.4
--- EvioXMLDictionary.java	28 Feb 2012 19:41:36 -0000	1.3
+++ EvioXMLDictionary.java	3 Apr 2013 20:07:46 -0000	1.4
@@ -3,52 +3,196 @@
 import java.io.File;
 import java.io.IOException;
 import java.io.ByteArrayInputStream;
-import java.util.Collections;
-import java.util.Vector;
+import java.io.StringWriter;
+import java.util.LinkedHashMap;
 
 import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.parsers.ParserConfigurationException;
 
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
 import org.w3c.dom.Document;
 import org.w3c.dom.NamedNodeMap;
 import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
 import org.xml.sax.SAXException;
 
+
+
 /**
  * This was developed to read the xml dictionary that Maurizio uses for GEMC.
  * It implements INameProvider, just like all other dictionary readers.
+ *
+ * An assumption in the following class is that each unique tag/num pair
+ * corresponds to an equally unique name. In other words, 2 different
+ * tag/numm pairs cannot have the same name.
  * 
  * @author heddle
- * 
+ * @author timmer
  */
 @SuppressWarnings("serial")
-public class EvioXMLDictionary extends Vector<EvioDictionaryEntry> implements INameProvider {
+public class EvioXMLDictionary implements INameProvider {
 
-	/**
-	 * There is only one type of element. This is its strange name.
-	 */
-	private static String ENTRY = "xmldumpDictEntry";
+    /** Element containing entire dictionary. */
+    private static String DICT_TOP_LEVEL = "xmlDict";
 
-	/**
-	 * The "name" attribute name.
-	 */
+    /** There is only one type of element which directly defines an entry (strange name). */
+    private static String ENTRY = "xmldumpDictEntry";
+
+    /**
+     * New, alternate, shortened form of ENTRY.
+     * @since 4.0
+     */
+    private static String ENTRY_ALT = "dictEntry";
+
+    /**
+     * Hierarchical container element.
+     * @since 4.0
+     */
+    private static String ENTRY_BANK = "bank";
+
+    /**
+     * Hierarchical leaf element.
+     * @since 4.0
+     */
+    private static String ENTRY_LEAF = "leaf";
+
+    /**
+     * Description element.
+     * @since 4.0
+     */
+    private static String DESCRIPTION = "description";
+
+    /**
+     * The "format" attribute string.
+     * @since 4.0
+     */
+    private static String FORMAT = "format";
+
+    /**
+     * The "type" attribute string.
+     * @since 4.0
+     */
+    private static String TYPE = "type";
+
+	/** The "name" attribute string. */
 	private static String NAME = "name";
 
-	/**
-	 * The "tag" attribute name.
-	 */
+	/** The "tag" attribute string. */
 	private static String TAG = "tag";
 
-	/**
-	 * The "num" attribute name.
-	 */
-	private static String NUM = "num";
+    /** The "num" attribute string. */
+    private static String NUM = "num";
+
+
+
+    /**
+     * The character used to separate hierarchical parts of names.
+     * @since 4.0
+     */
+    private String delimiter = ".";
 
     /**
-     * Create an EvioXMLDictionary. This is not a standard CODA dictionary,
-     * but a format developed for use by GEMC.
+     * This is the heart of the dictionary in which a key is composed of a tag/num
+     * pair and its corresponding value is a name. Using a hashmap ensures entries
+     * are unique.
+     * @since 4.0
+     */
+    private LinkedHashMap<EntryData,String> dictMap = new LinkedHashMap<EntryData,String>(1000);
+
+    /**
+     * This is a hashmap in which the key is a name and the value is composed
+     * of a corresponding tag/num pair. It's the reverse of the dictMap mapping.
+     * @since 4.0
+     */
+    private LinkedHashMap<String,EntryData> reverseMap = new LinkedHashMap<String,EntryData>(1000);
+
+    /**
+     * Top level xml Node object of xml DOM representation of dictionary.
+     * @since 4.0
+     */
+    private Node topLevelDoc;
+
+
+    /**
+     * Class to facilitate use of entry data as a key or value in a hash table.
+     */
+    private final class EntryData {
+
+        private final Integer  tag;
+        private final Integer  num;
+
+        private DataType type;
+
+        private final String   format;
+        private final String   description;
+
+        public EntryData(int tag, int num) {
+            this(tag, num, null, null, null);
+        }
+
+        public EntryData(int tag, int num, String type) {
+            this(tag, num, type, null, null);
+        }
+
+        public EntryData(int tag, int num, String type, String description) {
+            this(tag, num, type, description, null);
+        }
+
+        public EntryData(int tag, int num, String type, String description, String format) {
+            this.tag  = tag;
+            this.num  = num;
+            this.format = format;
+            this.description = description;
+            this.type = null;
+            if (type != null) {
+                try {
+                    this.type = DataType.valueOf(type.toUpperCase());
+                }
+                catch (IllegalArgumentException e) {
+                }
+            }
+        }
+
+        public final int hashCode() {
+            int hashTag = tag.hashCode();
+            int hashNum = num.hashCode();
+
+            return (hashTag + hashNum) * hashNum + hashTag;
+        }
+
+        // Objects equal eachother if tag & num are the same
+        public final boolean equals(Object other) {
+            if (other instanceof EntryData) {
+                EntryData otherPair = (EntryData) other;
+                return(tag.equals(otherPair.tag) && num.equals(otherPair.num));
+            }
+
+            return false;
+        }
+
+        public final String toString() {
+            if (type == null) {
+                return "(tag=" + tag + ",num =" + num + ")";
+            }
+            return "(tag=" + tag + ",num =" + num + ",type=" + type + ")";
+        }
+
+        public final int getTag()  {return tag;}
+        public final int getNum()  {return num;}
+        public final DataType getType()        {return type;}
+        public final String   getFormat()      {return format;}
+        public final String   getDescription() {return description;}
+    }
+
+    /**
+     * Create an EvioXMLDictionary from an xml file.
      *
      * @param file file containing xml.
      */
@@ -57,8 +201,17 @@
     }
 
     /**
-     * Create an EvioXMLDictionary. This is not a standard CODA dictionary,
-     * but a format developed for use by GEMC.
+     * Create an EvioXMLDictionary from an xml file.
+     *
+     * @param file file containing xml.
+     * @param delimiter character used to separate hierarchical parts of names.
+     */
+    public EvioXMLDictionary(File file, String delimiter) {
+        this(getDomObject(file), delimiter);
+    }
+
+    /**
+     * Create an EvioXMLDictionary from an xml string.
      *
      * @param xmlString string containing xml.
      */
@@ -67,80 +220,525 @@
     }
 
     /**
-	 * Create an EvioXMLDictionary. This is not a standard CODA dictionary,
-     * but a format developed for use by GEMC.
-	 * 
+     * Create an EvioXMLDictionary from an xml string.
+     *
+     * @param xmlString string containing xml.
+     * @param delimiter character used to separate hierarchical parts of names.
+     */
+    public EvioXMLDictionary(String xmlString, String delimiter) {
+        this(getDomObject(xmlString), delimiter);
+    }
+
+    /**
+     * Create an EvioXMLDictionary from an xml Document object.
+	 *
 	 * @param domDocument DOM object representing xml dictionary.
 	 */
 	public EvioXMLDictionary(Document domDocument) {
-		super(100);
+        this(domDocument, null);
+    }
 
-		// try to get the XML dom document
-		if (domDocument != null) {
-			NodeList list = domDocument.getElementsByTagName(ENTRY);
-
-			if (list != null) {
-				for (int index = 0; index < list.getLength(); index++) {
-					Node node = list.item(index);
-					if (node != null) {
-						if (node.hasAttributes()) {
-							String name = null;
-							String tag = null;
-							String num = null;
-
-							NamedNodeMap map = node.getAttributes();
-
-							// get the name
-							Node nameNode = map.getNamedItem(NAME);
-							if (nameNode != null) {
-								name = nameNode.getNodeValue();
-							}
-
-							// get the tag
-							Node tagNode = map.getNamedItem(TAG);
-							if (tagNode != null) {
-								tag = tagNode.getNodeValue();
-							}
-
-							// get the num
-							Node numNode = map.getNamedItem(NUM);
-							if (numNode != null) {
-								num = numNode.getNodeValue();
-							}
-							
-							add (new EvioDictionaryEntry(tag, num, name));
-						} // end node has attributes
-					} // end node not null
-				} // end loop over list
-			}
-		}
-		
-		//sort
-		Collections.sort(this);
-		
-//System.out.println(this);
+    /**
+     * Create an EvioXMLDictionary from an xml Document object.
+     *
+     * @param domDocument DOM object representing xml dictionary.
+     * @param delimiter character used to separate hierarchical parts of names.
+     */
+    public EvioXMLDictionary(Document domDocument, String delimiter) {
+
+        if (domDocument == null) return;
+        if (delimiter   != null) this.delimiter = delimiter;
+
+        // Find top level of dictionary inside xml file being parsed (case sensitive!)
+        NodeList list = domDocument.getElementsByTagName(DICT_TOP_LEVEL);
+
+        // If no dictionary, just return
+        if (list.getLength() < 1) return;
+
+        // Otherwise, pick the first one
+        Node topNode = list.item(0);
+        topLevelDoc = topNode;
+
+        // Look at all the children, if any
+        NodeList kidList = topNode.getChildNodes();
+        if (kidList.getLength() < 1) return;
+
+        int tag, num;
+        boolean badEntry;
+        String name, tagStr, numStr, type, format, description;
+
+        // Pick out elements that are both old & new direct entry elements
+        for (int index = 0; index < kidList.getLength(); index++) {
+            Node node = kidList.item(index);
+            if (node == null) continue;
+
+            // Only looking for "xmldumpDictEntry" and "dictEntry" nodes
+            if (!node.getNodeName().equalsIgnoreCase(ENTRY) &&
+                !node.getNodeName().equalsIgnoreCase(ENTRY_ALT)) {
+                continue;
+            }
+            if (node.hasAttributes()) {
+                tag = num = 0;
+                badEntry = false;
+                name = tagStr = type = format = description = null;
+
+                NamedNodeMap map = node.getAttributes();
+
+                // Get the name
+                Node attrNode = map.getNamedItem(NAME);
+                if (attrNode != null) {
+                    name = attrNode.getNodeValue();
+                }
+
+                // Get the tag
+                attrNode = map.getNamedItem(TAG);
+                if (attrNode != null) {
+                    tagStr = attrNode.getNodeValue();
+                    try {
+                        tag = Integer.parseInt(tagStr);
+                        if (tag < 0) badEntry = true;
+                    }
+                    catch (NumberFormatException e) {
+                        badEntry = true;
+                    }
+                }
+
+                // Get the num
+                attrNode = map.getNamedItem(NUM);
+                if (attrNode != null) {
+                    numStr = attrNode.getNodeValue();
+                    try {
+                        num = Integer.parseInt(numStr);
+                        if (num < 0) badEntry = true;
+                    }
+                    catch (NumberFormatException e) {
+                        badEntry = true;
+                    }
+                }
+
+                // Get the type, if any
+                attrNode = map.getNamedItem(TYPE);
+                if (attrNode != null) {
+                    type = attrNode.getNodeValue();
+                }
+
+                // Look for description node (xml element) as child of entry node
+                NodeList children = node.getChildNodes();
+                if (children.getLength() > 0) {
+
+                    // Pick out first description element only
+                    for (int i = 0; i < children.getLength(); i++) {
+                        Node childNode = children.item(i);
+                        if (childNode == null) continue;
+
+                        // Only looking for "description" node
+                        if (!childNode.getNodeName().equalsIgnoreCase(DESCRIPTION)) {
+                            continue;
+                        }
+
+                        description = childNode.getTextContent();
+//System.out.println("FOUND DESCRIPTION: = " + description);
+
+                        // See if there's a format attribute
+                        if (childNode.hasAttributes()) {
+                            map = childNode.getAttributes();
+
+                            // Get the format
+                            attrNode = map.getNamedItem(FORMAT);
+                            if (attrNode != null) {
+                                format = attrNode.getNodeValue();
+//System.out.println("FOUND FORMAT: = " + format);
+                            }
+                        }
+                        break;
+                    }
+                }
+
+                    // Skip meaningless entries
+                if (name == null || tagStr == null || badEntry) {
+                    System.out.println("IGNORING badly formatted dictionary entry: name = " + name);
+                    continue;
+                }
+
+                // Transform tag/num pair into single object
+                EntryData key = new EntryData(tag, num, type, description, format);
+
+                // Only add to dictionary if both name and tag/num pair are unique
+                if (!dictMap.containsKey(key) && !reverseMap.containsKey(name)) {
+                    dictMap.put(key, name);
+                    reverseMap.put(name, key);
+                }
+                else {
+                    System.out.println("IGNORING duplicate dictionary entry: name = " + name);
+                }
+            }
+        }
+
+        // Look at the (new) hierarchical entry elements,
+        // recursively, and add all existing entries.
+        addHierachicalDictEntries(kidList, null);
 
 	} // end Constructor
 
-	
-	/**
-	 * Returns the pretty name of some evio structure. Typically this involves
-	 * the use of the "tag" and, if present, "num" fields. There may also be a hierarchical
-	 * dependence. 
-	 * 
-	 * @param structure the structure to find the name of.
-	 * @return a descriptive name, e.g., "Edep".
-	 */
-	@Override
-	public String getName(BaseStructure structure) {
-		for (EvioDictionaryEntry entry : this) {
-			if (entry.match(structure)) {
-				return entry.getDescription(); 
-			}
-		}
-		return INameProvider.NO_NAME_STRING;
+
+
+    /**
+     * Takes a list of the children of an xml node, selects the new
+     * hierachical elements and converts them into a number of dictionary
+     * entries which are added to this object.
+     * This method acts recursively since any node may contain children.
+     *
+     * @param kidList a list of the children of an xml node.
+     */
+    private void addHierachicalDictEntries(NodeList kidList, String parentName) {
+
+        if (kidList == null || kidList.getLength() < 1) return;
+
+        int tag, num;
+        boolean isLeaf, badEntry;
+        String name, tagStr, numStr, type, format, description;
+
+        for (int i = 0; i < kidList.getLength(); i++) {
+
+            Node node = kidList.item(i);
+            if (node == null) continue;
+
+            isLeaf = node.getNodeName().equalsIgnoreCase(ENTRY_LEAF);
+
+            // Only looking for "bank" and "leaf" nodes
+            if (!node.getNodeName().equalsIgnoreCase(ENTRY_BANK) && !isLeaf) {
+                continue;
+            }
+
+            if (node.hasAttributes()) {
+                tag = num = 0;
+                badEntry = false;
+                name = tagStr = type = format = description = null;
+
+                NamedNodeMap map = node.getAttributes();
+
+                // Get the name
+                Node attrNode = map.getNamedItem(NAME);
+                if (attrNode != null) {
+                    name = attrNode.getNodeValue();
+                }
+
+                // Get the tag
+                Node tagNode = map.getNamedItem(TAG);
+                if (tagNode != null) {
+                    tagStr = tagNode.getNodeValue();
+                    try {
+                        tag = Integer.parseInt(tagStr);
+                        if (tag < 0) badEntry = true;
+                    }
+                    catch (NumberFormatException e) {
+                        badEntry = true;
+                    }
+                }
+
+                // Get the num
+                Node numNode = map.getNamedItem(NUM);
+                if (numNode != null) {
+                    numStr = numNode.getNodeValue();
+                    try {
+                        num = Integer.parseInt(numStr);
+                        if (num < 0) badEntry = true;
+                    }
+                    catch (NumberFormatException e) {
+                        badEntry = true;
+                    }
+                }
+
+                // Get the type, if any
+                attrNode = map.getNamedItem(TYPE);
+                if (attrNode != null) {
+                    type = attrNode.getNodeValue();
+                }
+
+                // Look for description node (xml element) as child of entry node
+                NodeList children = node.getChildNodes();
+                if (children.getLength() > 0) {
+
+                    // Pick out first description element only
+                    for (int j = 0; j < children.getLength(); j++) {
+                        Node childNode = children.item(j);
+                        if (childNode == null) continue;
+
+                        // Only looking for "description" node
+                        if (!childNode.getNodeName().equalsIgnoreCase(DESCRIPTION)) {
+                            continue;
+                        }
+
+                        description = childNode.getTextContent();
+//System.out.println("FOUND DESCRIPTION: = " + description);
+
+                        // See if there's a format attribute
+                        if (childNode.hasAttributes()) {
+                            map = childNode.getAttributes();
+
+                            // Get the format
+                            attrNode = map.getNamedItem(FORMAT);
+                            if (attrNode != null) {
+                                format = attrNode.getNodeValue();
+//System.out.println("FOUND FORMAT: = " + format);
+                            }
+                        }
+                        break;
+                    }
+                }
+
+                // Skip meaningless entries
+                if (name == null || tagStr == null || badEntry) {
+                    System.out.println("IGNORING badly formatted dictionary entry: name = " + name);
+                    continue;
+                }
+
+                // Create hierarchical name
+                if (parentName != null) name = parentName + delimiter + name;
+
+                // Transform tag/num pair into single object
+                EntryData key = new EntryData(tag, num, type, description, format);
+
+                // Only add to dictionary if both name and tag/num pair are unique
+                if (!dictMap.containsKey(key) && !reverseMap.containsKey(name)) {
+                    dictMap.put(key, name);
+                    reverseMap.put(name, key);
+
+                    // Look at this node's children recursively but skip a leaf's kids
+                    if (!isLeaf) {
+                        addHierachicalDictEntries(node.getChildNodes(), name);
+                    }
+                    else if (node.hasChildNodes()) {
+                        System.out.println("IGNORING children of \"leaf\" element " + name);
+                    }
+                }
+                else {
+                    System.out.println("IGNORING duplicate dictionary entry: name = " + name);
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Returns the name of a given evio structure.
+     *
+     * @param structure the structure to find the name of.
+     * @return a descriptive name or ??? if none found
+     */
+    @Override
+    public String getName(BaseStructure structure) {
+        int tag = structure.getHeader().getTag();
+        int num = structure.getHeader().getNumber();
+        return getName(tag, num);
+	}
+
+
+    /**
+     * Returns the name associated with the given tag and num.
+     *
+     * @param tag to find the name of
+     * @param num to find the name of
+     * @return a descriptive name or ??? if none found
+     */
+    public String getName(int tag, int num) {
+        EntryData key = new EntryData(tag, num);
+
+        String name = dictMap.get(key);
+        if (name == null) return INameProvider.NO_NAME_STRING;
+        return name;
+	}
+
+
+    /**
+     * Returns the description, if any, associated with the given tag and num.
+     *
+     * @param tag to find the description of
+     * @param num to find the description of
+     * @return the description or null if none found
+     */
+    public String getDescription(int tag, int num) {
+        // Need to find the description associated with given tag/num.
+        // First create an EntryData object to get associated name.
+        // (This works because of overriding the equals() method).
+        EntryData key = new EntryData(tag, num);
+
+        String name = dictMap.get(key);
+        if (name == null) {
+            System.out.println("getDescription: no entry for that key (tag/num)");
+            return null;
+        }
+
+        // Now that we have the name, use that to find the
+        // original EntryData object with the description in it.
+        EntryData origKey = reverseMap.get(name);
+        if (origKey == null) {
+            System.out.println("getDescription: no orig entry for that key (tag/num)");
+            return null;
+        }
+
+        return origKey.getDescription();
+	}
+
+
+    /**
+     * Returns the description, if any, associated with the name of a dictionary entry.
+     *
+     * @param name dictionary name
+     * @return description; null if name or is unknown or no description is associated with it
+     */
+    public String getDescription(String name) {
+        EntryData entry = reverseMap.get(name);
+        if (entry == null) {
+            return null;
+        }
+
+        return entry.getDescription();
+	}
+
+
+    /**
+     * Returns the format, if any, associated with the given tag and num.
+     *
+     * @param tag to find the format of
+     * @param num to find the format of
+     * @return the format or null if none found
+     */
+    public String getFormat(int tag, int num) {
+        // Need to find the format associated with given tag/num.
+        // First create an EntryData object to get associated name.
+        // (This works because of overriding the equals() method).
+        EntryData key = new EntryData(tag, num);
+
+        String name = dictMap.get(key);
+        if (name == null) return null;
+
+        // Now that we have the name, use that to find the
+        // original EntryData object with the format in it.
+        EntryData origKey = reverseMap.get(name);
+        if (origKey == null) {
+            return null;
+        }
+
+        return origKey.getFormat();
 	}
 
+
+    /**
+     * Returns the format, if any, associated with the name of a dictionary entry.
+     *
+     * @param name dictionary name
+     * @return format; null if name or is unknown or no format is associated with it
+     */
+    public String getFormat(String name) {
+        EntryData data = reverseMap.get(name);
+        if (data == null) {
+            return null;
+        }
+
+        return data.getFormat();
+	}
+
+
+
+    /**
+     * Returns the type, if any, associated with the given tag and num.
+     *
+     * @param tag to find the type of
+     * @param num to find the type of
+     * @return the type or null if none found
+     */
+    public DataType getType(int tag, int num) {
+        // Need to find the type associated with given tag/num.
+        // First create an EntryData object to get associated name.
+        // (This works because of overriding the equals() method).
+        EntryData key = new EntryData(tag, num);
+
+        String name = dictMap.get(key);
+        if (name == null) return null;
+
+        // Now that we have the name, use that to find the
+        // original EntryData object with the type in it.
+        EntryData origKey = reverseMap.get(name);
+        if (origKey == null) {
+            return null;
+        }
+
+        return origKey.getType();
+	}
+
+
+    /**
+     * Returns the type, if any, associated with the name of a dictionary entry.
+     *
+     * @param name dictionary name
+     * @return type; null if name or is unknown or no type is associated with it
+     */
+    public DataType getType(String name) {
+        EntryData data = reverseMap.get(name);
+        if (data == null) {
+            return null;
+        }
+
+        return data.getType();
+	}
+
+
+
+
+    /**
+     * Returns the tag/num pair, in an int array,
+     * corresponding to the name of a dictionary entry.
+     *
+     * @param name dictionary name
+     * @return an integer array in which the first element is the tag
+     *         and the second is the num; null if name is unknown
+     */
+    public int[] getTagNum(String name) {
+        // Get the tag/num pair
+        EntryData pair = reverseMap.get(name);
+        if (pair == null) {
+            return null;
+        }
+
+        return new int[] {pair.getTag(), pair.getNum()};
+	}
+
+
+    /**
+     * Returns the tag corresponding to the name of a dictionary entry.
+     *
+     * @param name dictionary name
+     * @return tag; -1 if name unknown
+     */
+    public int getTag(String name) {
+        // Get the data
+        EntryData data = reverseMap.get(name);
+        if (data == null) {
+            return -1;
+        }
+
+        return data.getTag();
+	}
+
+
+    /**
+     * Returns the num corresponding to the name of a dictionary entry.
+     *
+     * @param name dictionary name
+     * @return num; -1 if name unknown
+     */
+    public int getNum(String name) {
+        // Get the data
+        EntryData data = reverseMap.get(name);
+        if (data == null) {
+            return -1;
+        }
+
+        return data.getNum();
+	}
+
+
 	/**
 	 * Return a dom object corresponding to an xml file.
 	 * 
@@ -153,13 +751,11 @@
 		Document dom = null;
 
 		try {
-
 			// Using factory get an instance of document builder
 			DocumentBuilder db = dbf.newDocumentBuilder();
 
 			// parse using builder to get DOM representation of the XML file
 			dom = db.parse(file);
-
 		}
 		catch (ParserConfigurationException e) {
 			e.printStackTrace();
@@ -174,6 +770,7 @@
 		return dom;
 	}
 
+
     /**
      * Return a dom object corresponding to an xml string.
      *
@@ -186,14 +783,12 @@
         Document dom = null;
 
         try {
-
             // Using factory get an instance of document builder
             DocumentBuilder db = dbf.newDocumentBuilder();
 
             // parse using builder to get DOM representation of the XML string
             ByteArrayInputStream bais = new ByteArrayInputStream(xmlString.getBytes());
             dom = db.parse(bais);
-
         }
         catch (ParserConfigurationException e) {
             e.printStackTrace();
@@ -208,32 +803,51 @@
         return dom;
     }
 
+
     /**
      * Get an xml representation of the dictionary.
      * @return an xml representation of the dictionary.
      */
     public String toXML() {
-        StringBuilder sb = new StringBuilder(4096);
-        sb.append("<!-- Evio Dictionary: mapping string to tag & num values -->\n");
-        sb.append("<xmlDict>\n\n");
-        for (EvioDictionaryEntry entry : this) {
-            sb.append(entry.toXML());
-        }
-        sb.append("\n</xmlDict>\n");
-        return sb.toString();
+        return nodeToString(topLevelDoc);
     }
 
+
+    /**
+     * This method converts an xml Node object into an xml string.
+     * @return an xml representation of an xml Node object; null if conversion problems
+     */
+    private String nodeToString(Node node) {
+          StringWriter sw = new StringWriter();
+          try {
+              TransformerFactory transformerFactory = TransformerFactory.newInstance();
+              transformerFactory.setAttribute("indent-number", 3);
+              Transformer t = transformerFactory.newTransformer();
+              t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
+              t.setOutputProperty(OutputKeys.INDENT, "yes");
+              t.transform(new DOMSource(node), new StreamResult(sw));
+          } catch (TransformerException te) {
+              return null;
+          }
+          return sw.toString();
+    }
+
+
     /**
      * Get a string representation of the dictionary.
      * @return a string representation of the dictionary.
      */
     @Override
     public String toString() {
+        EntryData pair;
         StringBuilder sb = new StringBuilder(4096);
         sb.append("-- Dictionary --\n");
-        for (EvioDictionaryEntry entry : this) {
-            sb.append(entry.toString());
-            sb.append("\n");
+
+        for (String name : reverseMap.keySet()) {
+            // Get the tag/num pair
+            pair = reverseMap.get(name);
+            sb.append(String.format("tag: %-15s num: %-15s name: %s\n", pair.tag, pair.num, name));
+
         }
         return sb.toString();
     }

jevio-base/src/main/java/org/jlab/coda/jevio
StructureTransformer.java 1.1 -> 1.2
diff -u -r1.1 -r1.2
--- StructureTransformer.java	28 Feb 2012 19:41:36 -0000	1.1
+++ StructureTransformer.java	3 Apr 2013 20:07:46 -0000	1.2
@@ -19,7 +19,7 @@
      * @param num num of the created EvioBank.
      * @return the created EvioBank.
      */
-    public EvioBank transform(EvioSegment segment, int num) {
+    static public EvioBank transform(EvioSegment segment, int num) {
         BaseStructureHeader header = segment.getHeader();
         DataType type = header.getDataType();
 
@@ -28,7 +28,7 @@
         bankHeader.setPadding(header.getPadding());
 
         EvioBank bank = new EvioBank(bankHeader);
-        bank.copy(segment);
+        bank.transform(segment);
         return bank;
     }
 
@@ -48,7 +48,7 @@
      * @param num num of the created EvioBank.
      * @return the created EvioBank.
      */
-    public EvioBank transform(EvioTagSegment tagsegment, int num) {
+    static public EvioBank transform(EvioTagSegment tagsegment, int num) {
         BaseStructureHeader header = tagsegment.getHeader();
         DataType type = header.getDataType();
 
@@ -57,7 +57,7 @@
         bankHeader.setPadding(header.getPadding());
 
         EvioBank bank = new EvioBank(bankHeader);
-        bank.copy(tagsegment);
+        bank.transform(tagsegment);
         return bank;
     }
 
@@ -75,7 +75,7 @@
      * @param segment EvioSegment object to transform.
      * @return the created EvioTagSegment.
      */
-    public EvioTagSegment transform(EvioSegment segment) {
+    static public EvioTagSegment transform(EvioSegment segment) {
         BaseStructureHeader segHeader = segment.getHeader();
         DataType type;
         DataType segType = type = segHeader.getDataType();
@@ -94,7 +94,7 @@
         tagsegHeader.setLength(segHeader.getLength());
 
         EvioTagSegment tagseg = new EvioTagSegment(tagsegHeader);
-        tagseg.copy(segment);
+        tagseg.transform(segment);
         return tagseg;
     }
 
@@ -114,7 +114,7 @@
      * @param tagsegment EvioTagSegment object to transform.
      * @return the created EvioSegment.
      */
-    public EvioSegment transform(EvioTagSegment tagsegment) {
+    static public EvioSegment transform(EvioTagSegment tagsegment) {
         BaseStructureHeader tagsegHeader = tagsegment.getHeader();
         DataType type = tagsegHeader.getDataType();
 
@@ -125,7 +125,7 @@
         segHeader.setPadding(tagsegHeader.getPadding());
 
         EvioSegment seg = new EvioSegment(segHeader);
-        seg.copy(tagsegment);
+        seg.transform(tagsegment);
         return seg;
     }
 
@@ -145,7 +145,7 @@
      * @return the created EvioSegment.
      * @throws EvioException if the bank is too long to change into a segment
      */
-    public EvioSegment transform(EvioBank bank) throws EvioException {
+    static public EvioSegment transform(EvioBank bank) throws EvioException {
         BaseStructureHeader header = bank.getHeader();
         if (header.getLength() > 65535) {
             throw new EvioException("Bank is too long to transform into segment");
@@ -158,7 +158,7 @@
         segHeader.setPadding(header.getPadding());
 
         EvioSegment seg = new EvioSegment(segHeader);
-        seg.copy(bank);
+        seg.transform(bank);
         return seg;
     }
 
@@ -179,7 +179,7 @@
      * @return the created EvioTagSegment.
      * @throws EvioException if the bank is too long to change into a tagsegment
      */
-    public EvioTagSegment transform(EvioBank bank, int dummy) throws EvioException {
+    static public EvioTagSegment transform(EvioBank bank, int dummy) throws EvioException {
         BaseStructureHeader header = bank.getHeader();
         if (header.getLength() > 65535) {
             throw new EvioException("Bank is too long to transform into tagsegment");
@@ -202,7 +202,7 @@
         tagsegHeader.setPadding(header.getPadding());
 
         EvioTagSegment tagseg = new EvioTagSegment(tagsegHeader);
-        tagseg.copy(bank);
+        tagseg.transform(bank);
         return tagseg;
     }
 }

jevio-base/src/main/java/org/jlab/coda/jevio
EventBuilder.java 1.3 -> 1.4
diff -u -r1.3 -r1.4
--- EventBuilder.java	28 Feb 2012 19:41:36 -0000	1.3
+++ EventBuilder.java	3 Apr 2013 20:07:46 -0000	1.4
@@ -43,10 +43,16 @@
 	/**
 	 * This goes through the event recursively, and makes sure all the length fields
 	 * in the headers are properly set.
+     * @throws EvioException if the length of containing event is too large (> Integer.MAX_VALULE),
 	 */
 	public void setAllHeaderLengths() {
-		event.setAllHeaderLengths();
-	}
+        try {
+            event.setAllHeaderLengths();
+        }
+        catch (EvioException e) {
+            e.printStackTrace();
+        }
+    }
 		
 	/**
 	 * This clears all the data fields in a structure, but not the parent or the children. This keeps the
@@ -75,13 +81,13 @@
      *
 	 * @param parent the parent structure.
 	 * @param child the child structure.
-	 * @throws EvioException if child is null, has wrong byte order,
+	 * @throws EvioException if parent or child is null, child has wrong byte order,
      *                       is wrong structure type, or parent is not a container
 	 */
 	public void addChild(BaseStructure parent, BaseStructure child) throws EvioException {
 		
-		if (child == null) {
-			throw new EvioException("Attempt to add null child in addChild.");
+		if (child == null || parent == null) {
+			throw new EvioException("Null child or parent arg.");
 		}
 
         if (child.getByteOrder() != event.getByteOrder()) {
@@ -127,6 +133,7 @@
 		
 
 		parent.insert(child);
+        child.setParent(parent);
 		setAllHeaderLengths();
 	}
 	
@@ -148,24 +155,139 @@
 		}
 				
 		child.removeFromParent();
+        child.setParent(null);
 		setAllHeaderLengths();
 	}
-	
+
+
 	/**
-	 * Appends int data to the structure. If the structure has no data, then this
-	 * is the same as setting the data.
-	 * @param structure the structure to receive the data, which is appended.
-	 * @param data the int data to append, or set if there is no existing data.
-	 * @throws EvioException
+	 * Set int data to the structure. If the structure has data, it is overwritten
+     * even if the existing data is of a different type.
+	 * @param structure the structure to receive the data.
+	 * @param data the int data to write.
+	 * @throws EvioException if structure arg is null
 	 */
-	public void appendIntData(BaseStructure structure, int data[]) throws EvioException {
+	public void setIntData(BaseStructure structure, int data[]) throws EvioException {
 		if (structure == null) {
-			throw new EvioException("Tried to append int data to a null structure.");
+			throw new EvioException("Tried to set int data to a null structure.");
 		}
-		structure.appendIntData(data);
+		structure.setIntData(data);
 		setAllHeaderLengths();
 	}
-	
+
+
+    /**
+     * Set short data to the structure. If the structure has data, it is overwritten
+     * even if the existing data is of a different type.
+     * @param structure the structure to receive the data.
+     * @param data the short data to write.
+     * @throws EvioException if structure arg is null
+     */
+    public void setShortData(BaseStructure structure, short data[]) throws EvioException {
+        if (structure == null) {
+            throw new EvioException("Tried to set short data to a null structure.");
+        }
+        structure.setShortData(data);
+        setAllHeaderLengths();
+    }
+
+
+    /**
+     * Set long data to the structure. If the structure has data, it is overwritten
+     * even if the existing data is of a different type.
+     * @param structure the structure to receive the data.
+     * @param data the long data to write.
+     * @throws EvioException if structure arg is null
+     */
+    public void setLongData(BaseStructure structure, long data[]) throws EvioException {
+        if (structure == null) {
+            throw new EvioException("Tried to set long data to a null structure.");
+        }
+        structure.setLongData(data);
+        setAllHeaderLengths();
+    }
+
+
+    /**
+     * Set byte data to the structure. If the structure has data, it is overwritten
+     * even if the existing data is of a different type.
+     * @param structure the structure to receive the data.
+     * @param data the byte data to write.
+     * @throws EvioException if structure arg is null
+     */
+    public void setByteData(BaseStructure structure, byte data[]) throws EvioException {
+        if (structure == null) {
+            throw new EvioException("Tried to set byte data to a null structure.");
+        }
+        structure.setByteData(data);
+        setAllHeaderLengths();
+    }
+
+
+    /**
+     * Appends int data to the structure. If the structure has no data, then this
+     * is the same as setting the data.
+     * @param structure the structure to receive the data, which is appended.
+     * @param data the int data to append, or set if there is no existing data.
+     * @throws EvioException
+     */
+    public void appendIntData(BaseStructure structure, int data[]) throws EvioException {
+        if (structure == null) {
+            throw new EvioException("Tried to append int data to a null structure.");
+        }
+        structure.appendIntData(data);
+        setAllHeaderLengths();
+    }
+
+
+    /**
+     * Set float data to the structure. If the structure has data, it is overwritten
+     * even if the existing data is of a different type.
+     * @param structure the structure to receive the data.
+     * @param data the float data to write.
+     * @throws EvioException if structure arg is null
+     */
+    public void setFloatData(BaseStructure structure, float data[]) throws EvioException {
+        if (structure == null) {
+            throw new EvioException("Tried to set float data to a null structure.");
+        }
+        structure.setFloatData(data);
+        setAllHeaderLengths();
+    }
+
+
+    /**
+     * Set double data to the structure. If the structure has data, it is overwritten
+     * even if the existing data is of a different type.
+     * @param structure the structure to receive the data.
+     * @param data the double data to write.
+     * @throws EvioException if structure arg is null
+     */
+    public void setIntData(BaseStructure structure, double data[]) throws EvioException {
+        if (structure == null) {
+            throw new EvioException("Tried to set double data to a null structure.");
+        }
+        structure.setDoubleData(data);
+        setAllHeaderLengths();
+    }
+
+
+    /**
+     * Set string data to the structure. If the structure has data, it is overwritten
+     * even if the existing data is of a different type.
+     * @param structure the structure to receive the data.
+     * @param data the string data to write.
+     * @throws EvioException if structure arg is null
+     */
+    public void setStringData(BaseStructure structure, String data[]) throws EvioException {
+        if (structure == null) {
+            throw new EvioException("Tried to set string data to a null structure.");
+        }
+        structure.setStringData(data);
+        setAllHeaderLengths();
+    }
+
+
 	/**
 	 * Appends short data to the structure. If the structure has no data, then this
 	 * is the same as setting the data.
@@ -181,7 +303,7 @@
 		setAllHeaderLengths();
 	}
 
-	
+
 	/**
 	 * Appends long data to the structure. If the structure has no data, then this
 	 * is the same as setting the data.
@@ -196,7 +318,8 @@
 		structure.appendLongData(data);
 		setAllHeaderLengths();
 	}
-	
+
+
 	/**
 	 * Appends byte data to the structure. If the structure has no data, then this
 	 * is the same as setting the data.
@@ -211,7 +334,8 @@
 		structure.appendByteData(data);
 		setAllHeaderLengths();
 	}
-	
+
+
 	/**
 	 * Appends float data to the structure. If the structure has no data, then this
 	 * is the same as setting the data.
@@ -227,21 +351,22 @@
 		setAllHeaderLengths();
 	}
 	
-//    /**
-//     * Appends char data to the structure. If the structure has no data, then this
-//     * is the same as setting the data.
-//     * @param structure the structure to receive the data, which is appended.
-//     * @param data the char data to append, or set if there is no existing data.
-//     * @throws EvioException
-//     */
-//    public void appendCharData(BaseStructure structure, char data[]) throws EvioException {
-//
-//        if (structure == null) {
-//            throw new EvioException("Tried to append char data to a null structure.");
-//        }
-//        structure.appendCharData(data);
-//        setAllHeaderLengths();
-//    }
+
+    /**
+     * Appends double data to the structure. If the structure has no data, then this
+     * is the same as setting the data.
+     * @param structure the structure to receive the data, which is appended.
+     * @param data the double data to append, or set if there is no existing data.
+     * @throws EvioException
+     */
+    public void appendDoubleData(BaseStructure structure, double data[]) throws EvioException {
+        if (structure == null) {
+            throw new EvioException("Tried to append double data to a null structure.");
+        }
+        structure.appendDoubleData(data);
+        setAllHeaderLengths();
+    }
+
 
     /**
      * Appends string data to the structure. If the structure has no data, then this
@@ -261,21 +386,6 @@
 
 
 	/**
-	 * Appends double data to the structure. If the structure has no data, then this
-	 * is the same as setting the data.
-	 * @param structure the structure to receive the data, which is appended.
-	 * @param data the double data to append, or set if there is no existing data.
-	 * @throws EvioException
-	 */
-	public void appendDoubleData(BaseStructure structure, double data[]) throws EvioException {
-		if (structure == null) {
-			throw new EvioException("Tried to append double data to a null structure.");
-		}
-		structure.appendDoubleData(data);
-        setAllHeaderLengths();
-	}
-	
-	/**
 	 * Get the underlying event.
 	 * @return the underlying event.
 	 */

jevio-base/src/main/java/org/jlab/coda/jevio
BlockHeaderV4.java 1.2 -> 1.3
diff -u -r1.2 -r1.3
--- BlockHeaderV4.java	20 Mar 2012 23:21:49 -0000	1.2
+++ BlockHeaderV4.java	3 Apr 2013 20:07:46 -0000	1.3
@@ -39,7 +39,7 @@
  *
  *
  *      Block Length       = number of ints in block (including this one).
- *      Block Number       = id number
+ *      Block Number       = id number (starting at 1)
  *      Header Length      = number of ints in this header (8)
  *      Event Count        = number of events in this block (always an integral #).
  *                           NOTE: this value should not be used to parse the following
@@ -72,11 +72,40 @@
  */
 public class BlockHeaderV4 implements IEvioWriter, IBlockHeader {
 
-	/**
-	 * The maximum block size in 32 bit ints.
+    /** The minimum & expected block header size in 32 bit ints. */
+    public static final int HEADER_SIZE = 8;
+
+    /**
+     * The maximum block size in 32 bit ints.
      * Don't allow more than 40MBytes per block.
-	 */
-	public static final int MAX_BLOCK_SIZE = 10000000;
+     */
+    public static final int MAX_BLOCK_SIZE = 10000000;
+
+    /** Dictionary presence is 9th bit in version/info word */
+    public static final int EV_DICTIONARY_MASK = 0x100;
+
+    /** "Last block" is 10th bit in version/info word */
+    public static final int EV_LASTBLOCK_MASK  = 0x200;
+
+    /** Position of word for size of block in 32-bit words. */
+    public static final int EV_BLOCKSIZE = 0;
+    /** Position of word for block number, starting at 0. */
+    public static final int EV_BLOCKNUM = 1;
+    /** Position of word for size of header in 32-bit words (=8). */
+    public static final int EV_HEADERSIZE = 2;
+    /** Position of word for number of events in block. */
+    public static final int EV_COUNT = 3;
+    /** Position of word for reserved. */
+    public static final int EV_RESERVED1 = 4;
+    /** Position of word for version of file format. */
+    public static final int EV_VERSION = 5;
+    /** Position of word for reserved. */
+    public static final int EV_RESERVED2 = 6;
+    /** Position of word for magic number for endianness tracking. */
+    public static final int EV_MAGIC = 7;
+
+
+
 
 	/** The block (physical record) size in 32 bit ints. */
 	private int size;
@@ -164,6 +193,32 @@
         magicNumber = MAGIC_NUMBER;
     }
 
+    /**
+     * This copy constructor creates an evio version 4 BlockHeader
+     * from another object of this class.
+     * @param blkHeader block header object to copy
+     */
+    public BlockHeaderV4(BlockHeaderV4 blkHeader) {
+        if (blkHeader == null) {
+            return;
+        }
+        size         = blkHeader.size;
+        number       = blkHeader.number;
+        headerLength = blkHeader.headerLength;
+        version      = blkHeader.version;
+        eventCount   = blkHeader.eventCount;
+        reserved1    = blkHeader.reserved1;
+        reserved2    = blkHeader.reserved2;
+        magicNumber  = blkHeader.magicNumber;
+        bitInfo      = (BitSet)  blkHeader.bitInfo.clone();
+        bufferStartingPosition = blkHeader.bufferStartingPosition;
+    }
+
+    /*** {@inheritDoc}  */
+    public Object clone() {
+        return new BlockHeaderV4(this);
+    }
+
 	/**
 	 * Set the size of the block (physical record). Some trivial checking is done.
 	 *
@@ -238,7 +293,7 @@
 	 * param headerLength the new block header length. This should be 8.
 	 */
 	public void setHeaderLength(int headerLength) {
-		if (headerLength != 8) {
+		if (headerLength != HEADER_SIZE) {
             System.out.println("Warning: Block Header Length = " + headerLength);
 		}
 		this.headerLength = headerLength;
@@ -264,6 +319,16 @@
     }
 
     /**
+     * Does this integer indicate that there is an evio dictionary
+     * (assuming it's the header's sixth word)?
+     *
+     * @return <code>true</code> if this int's indicates an evio dictionary, else <code>false</code>
+     */
+    static public boolean hasDictionary(int i) {
+        return ((i & EV_DICTIONARY_MASK) > 0);
+    }
+
+    /**
      * Is this block's first event is an evio dictionary?
      *
      * @return <code>true</code> if this block's first event is an evio dictionary, else <code>false</code>
@@ -283,6 +348,35 @@
     }
 
     /**
+     * Does this integer indicate that this is the last block
+     * (assuming it's the header's sixth word)?
+     *
+     * @return <code>true</code> if this int's indicates the last block, else <code>false</code>
+     */
+    static public boolean isLastBlock(int i) {
+        return ((i & EV_LASTBLOCK_MASK) > 0);
+    }
+
+    /**
+     * Set the bit in the given arg which indicates this is the last block.
+     * @param i integer in which to set the last-block bit
+     * @return  arg with last-block bit set
+     */
+    static public int setLastBlockBit(int i)   {
+        return (i |= EV_LASTBLOCK_MASK);
+    }
+
+    /**
+     * Clear the last-block bit in the given arg to indicate it is NOT the last block.
+     * @param i integer in which to clear the last-block bit
+     * @return arg with last-block bit cleared
+     */
+    static public int clearLastBlockBit(int i) {
+        return (i &= ~EV_LASTBLOCK_MASK);
+    }
+
+
+    /**
      * Get the value of bits 2-5. If this header is coming from the ROC,
      * it represents the type of event being sent.
      *
@@ -334,15 +428,22 @@
     }
 
     /**
-     * Sets the right bits int the sixth word corresponding to the given type.
+     * Sets the right bits in bit set (2-5 when starting at 0)
+     * to hold 4 bits of the given type value. Useful when
+     * generating a bitset for use with {@link EventWriter}
+     * constructor.
+     *
+     * @param bSet Bitset containing all bits to be set
      * @param type event type as int
      */
-    public void setEventType(int type) {
+    static public void setEventType(BitSet bSet, int type) {
         if (type < 0) type = 0;
         else if (type > 15) type = 15;
 
+        if (bSet.size() < 6) return;
+
         for (int i=2; i < 6; i++) {
-            bitInfo.set(i, ((type >>> i-2) & 0x1) > 0);
+            bSet.set(i, ((type >>> i-2) & 0x1) > 0);
         }
     }
 
@@ -391,29 +492,62 @@
      * in the lowest 8 bits and the set in the upper 24 bits. The arg isDictionary
      * is set in the 9th bit and isEnd is set in the 10th bit.
      *
-     * @param set Bitset containing all bits to be set
+     * @param bSet Bitset containing all bits to be set
      * @param isDictionary does this block include an evio xml dictionary as the first event?
      * @param isEnd is this the last block of a file or a buffer?
      * @return generated sixth word of this header.
      */
-    static public int generateSixthWord(BitSet set, boolean isDictionary, boolean isEnd) {
+    static public int generateSixthWord(BitSet bSet, boolean isDictionary, boolean isEnd) {
         int v = 4; // version
 
-        for (int i=0; i < set.length(); i++) {
+        for (int i=0; i < bSet.length(); i++) {
             if (i > 23) {
                 break;
             }
-            if (set.get(i)) {
+            if (bSet.get(i)) {
                 v |= (0x1 << (8+i));
             }
         }
 
         v =  isDictionary ? (v | 0x100) : v;
         v =  isEnd ? (v | 0x200) : v;
+
         return v;
     }
 
     /**
+      * Calculates the sixth word of this header which has the version number (4)
+      * in the lowest 8 bits and the set in the upper 24 bits. The arg isDictionary
+      * is set in the 9th bit and isEnd is set in the 10th bit. Four bits of an int
+      * (event type) are set in bits 10-13.
+      *
+      * @param bSet Bitset containing all bits to be set
+      * @param isDictionary does this block include an evio xml dictionary as the first event?
+      * @param isEnd is this the last block of a file or a buffer?
+      * @param eventType 4 bit type of events header is containing
+      * @return generated sixth word of this header.
+      */
+     static public int generateSixthWord(BitSet bSet, boolean isDictionary,
+                                         boolean isEnd, int eventType) {
+         int v = 4; // version
+
+         for (int i=0; i < bSet.length(); i++) {
+             if (i > 23) {
+                 break;
+             }
+             if (bSet.get(i)) {
+                 v |= (0x1 << (8+i));
+             }
+         }
+
+         v =  isDictionary ? (v | 0x100) : v;
+         v =  isEnd ? (v | 0x200) : v;
+         v |= ((eventType & 0xf) << 10);
+
+         return v;
+     }
+
+    /**
      * Parses the argument into the bit info fields.
      * This ignores the version in the lowest 8 bits.
      *
@@ -434,6 +568,14 @@
     }
 
     /**
+     * Sets the value of first reserved word.
+     * @param reserved1 the value for first reserved word.
+     */
+    public void setReserved1(int reserved1) {
+        this.reserved1 = reserved1;
+    }
+
+    /**
      * Get the second reserved word.
      * @return the second reserved word.
      */
@@ -441,6 +583,14 @@
         return reserved2;
     }
 
+    /**
+     * Sets the value of second reserved word.
+     * @param reserved2 the value for second reserved word.
+     */
+    public void setReserved2(int reserved2) {
+        this.reserved2 = reserved2;
+    }
+
 	/**
 	 * Get the magic number the block (physical record) header which should be 0xc0da0100.
 	 *
@@ -479,7 +629,9 @@
 		sb.append(String.format("block size:     %d\n", size));
 		sb.append(String.format("number:         %d\n", number));
 		sb.append(String.format("headerLen:      %d\n", headerLength));
-		sb.append(String.format("event count:    %d\n", eventCount));
+        sb.append(String.format("event count:    %d\n", eventCount));
+        sb.append(String.format("reserved 1:     %d\n", reserved1));
+        sb.append(String.format("bit info:       %s\n", bitInfo));
         sb.append(String.format("has dictionary: %b\n", hasDictionary()));
         sb.append(String.format("version:        %d\n", version));
 		sb.append(String.format("magicNumber:    %8x\n", magicNumber));
@@ -546,7 +698,8 @@
 	/**
 	 * Gives the bytes remaining in this block (physical record) given a buffer position. The position is an absolute
 	 * position in a byte buffer. This assumes that the absolute position in <code>bufferStartingPosition</code> is
-	 * being maintained properly by the reader.
+	 * being maintained properly by the reader. No block is longer than 2.1GB - 31 bits of length. This is for
+     * practical reasons - so a block can be read into a single byte array.
 	 *
 	 * @param position the absolute current position is a byte buffer.
 	 * @return the number of bytes remaining in this block (physical record.)
@@ -558,7 +711,6 @@
 		}
 
 		int nextBufferStart = nextBufferStartingPosition();
-//System.out.println("bytesRemaining: position = " + position + ", next buffer start = " + nextBufferStart);
 		if (position > nextBufferStart) {
 			throw new EvioException("Provided position beyond buffer end position.");
 		}

jevio-base/src/main/java/org/jlab/coda/jevio
BaseStructureHeader.java 1.3 -> 1.4
diff -u -r1.3 -r1.4
--- BaseStructureHeader.java	28 Feb 2012 19:41:35 -0000	1.3
+++ BaseStructureHeader.java	3 Apr 2013 20:07:46 -0000	1.4
@@ -9,7 +9,7 @@
  * @author heddle
  * 
  */
-public abstract class BaseStructureHeader implements IEvioWriter {
+public abstract class BaseStructureHeader implements Cloneable, IEvioWriter {
 
     /**
      * The length of the structure in ints. This never includes the
@@ -20,7 +20,7 @@
 	/**
 	 * The structure tag.
 	 */
-	protected int tag = 0;
+	protected int tag;
 
     /**
      * The data type of the structure.
@@ -32,7 +32,7 @@
      * Allowed value is 0, 1, 2, or 3 (0,2 for shorts and 0-3 for bytes)
      * and is stored in the upper 2 bits of the dataType when written out.
      */
-    protected int padding = 0;
+    protected int padding;
 
 	/**
 	 * The number represents an unsigned byte. Only Banks have a number
@@ -68,6 +68,21 @@
 	}
 
 
+    /**
+     * Clone this object by passing responsibility to the Object class
+     * which creates a bitwise copy which is fine since all fields are
+     * ints or enums.
+     */
+    public Object clone() {
+        try {
+            return super.clone();
+        }
+        catch (CloneNotSupportedException e) {
+            return null;
+        }
+    }
+
+
 	/**
 	 * Get the number. Only Banks have a number field in their header, so this is only relevant for Banks.
 	 * 

jevio-base/src/main/java/org/jlab/coda/jevio
BankHeader.java 1.3 -> 1.4
diff -u -r1.3 -r1.4
--- BankHeader.java	28 Feb 2012 19:41:35 -0000	1.3
+++ BankHeader.java	3 Apr 2013 20:07:46 -0000	1.4
@@ -12,7 +12,7 @@
  * 
  */
 public final class BankHeader extends BaseStructureHeader {
-	
+
 	/**
 	 * Null constructor.
 	 */

jevio-base/src/main/java/org/jlab/coda/jevio
BaseStructure.java 1.6 -> 1.7
diff -u -r1.6 -r1.7
--- BaseStructure.java	16 Aug 2012 22:36:58 -0000	1.6
+++ BaseStructure.java	3 Apr 2013 20:07:46 -0000	1.7
@@ -6,6 +6,7 @@
 import java.io.StringWriter;
 
 import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.DefaultTreeModel;
 import javax.swing.tree.MutableTreeNode;
 import javax.swing.tree.TreeNode;
 import javax.xml.stream.XMLStreamException;
@@ -24,7 +25,7 @@
  * @author timmer - add byte order tracking, make setAllHeaderLengths more efficient
  *
  */
-public abstract class BaseStructure implements IEvioStructure, MutableTreeNode, IEvioWriter {
+public abstract class BaseStructure implements Cloneable, IEvioStructure, MutableTreeNode, IEvioWriter {
 
 	/**
 	 * Holds the header of the bank.
@@ -33,6 +34,8 @@
 
     /**
      * The raw data of the structure.
+     * Storing data as raw bytes limits the # of data elements to
+     * Integer.MAX_VALUE/size_of_data_type_in_bytes.
      */
     protected byte rawBytes[];
 
@@ -74,7 +77,7 @@
     /**
      * Used if raw data should be interpreted as composite type.
      */
-    protected CompositeData compositeData;
+    protected CompositeData[] compositeData;
 
     /**
      * Used if raw data should be interpreted as a string.
@@ -113,12 +116,12 @@
     /**
      * Name of this object as an XML element.
      */
-    protected String xmlElementName;
+    protected String xmlElementName = "unknown";   //TODO: get rid of this init val
 
     /**
      * Name of this object's contents as an XML attribute if it is a structure type.
      */
-    protected String xmlContentAttributeName;
+    protected String xmlContentAttributeName = "unknown32";  //TODO: get rid of this init val
 
     /**
      * Endianness of the raw data if appropriate,
@@ -163,14 +166,17 @@
 	}
 
     /**
-     * Copy all the data from another BaseStructure object.
-     * Although data is copied, children are not copied in the
-     * deep clone way, but only added to this structure.
-     * Does <b>not</b> copy header data.
+     * This method does a partial copy and is designed to help convert
+     * between banks, segments,and tagsegments in the {@link StructureTransformer}
+     * class (hence the name "transfrom").
+     * It copies all the data from another BaseStructure object.
+     * Children are <b>not</b> copied in the deep clone way,
+     * but their references are added to this structure.
+     * It does <b>not</b> copy header data or the parent either.
      *
      * @param structure BaseStructure from which to copy data.
      */
-    public void copy(BaseStructure structure) {
+    public void transform(BaseStructure structure) {
 
         if (structure == null) return;
 
@@ -262,7 +268,11 @@
 
             case COMPOSITE:
                 if (structure.compositeData != null) {
-                    compositeData = (CompositeData) structure.compositeData.clone();
+                    int len = structure.compositeData.length;
+                    compositeData = new CompositeData[len];
+                    for (int i=0; i < len; i++) {
+                        compositeData[i] = (CompositeData) structure.compositeData[i].clone();
+                    }
                 }
                 break;
 
@@ -280,6 +290,135 @@
         }
     }
 
+//    static public BaseStructure copy(BaseStructure structure) {
+//        return (BaseStructure)structure.clone();
+//    }
+
+    /**
+     * Clone this object. First call the Object class's clone() method
+     * which creates a bitwise copy. Then clone all the mutable objects
+     * so that this method does a safe (not deep) clone. This means all
+     * children get cloned as well. Remind me again why anyone would
+     * want to clone their bratty kids?
+     */
+    public Object clone() {
+        try {
+            // "bs" is a bitwise copy. Do a deep clone of all object references.
+            BaseStructure bs = (BaseStructure)super.clone();
+
+            // Clone the header
+            bs.header = (BaseStructureHeader) header.clone();
+
+            // Clone raw bytes
+            if (rawBytes != null) {
+                bs.rawBytes = rawBytes.clone();
+            }
+
+            // Clone data
+            switch (header.getDataType())  {
+                case SHORT16:
+                case USHORT16:
+                    if (shortData != null) {
+                        bs.shortData = shortData.clone();
+                    }
+                    break;
+
+                case INT32:
+                case UINT32:
+                    if (intData != null) {
+                        bs.intData = intData.clone();
+                    }
+                    break;
+
+                case LONG64:
+                case ULONG64:
+                    if (longData != null) {
+                        bs.longData = longData.clone();
+                    }
+                    break;
+
+                case FLOAT32:
+                    if (floatData != null) {
+                        bs.floatData = floatData.clone();
+                    }
+                    break;
+
+                case DOUBLE64:
+                    if (doubleData != null) {
+                        bs.doubleData = doubleData.clone();
+                    }
+                    break;
+
+                case CHAR8:
+                case UCHAR8:
+                    if (charData != null) {
+                        bs.charData = charData.clone();
+                    }
+                    break;
+
+                case CHARSTAR8:
+                    if (stringsList != null) {
+                        bs.stringsList = new LinkedList<String>();
+                        bs.stringsList.addAll(stringsList);
+                        bs.stringData = new StringBuffer(stringData);
+                        bs.stringEnd  = stringEnd;
+                    }
+                    break;
+
+                case COMPOSITE:
+                    if (compositeData != null) {
+                        int len = compositeData.length;
+                        bs.compositeData = new CompositeData[len];
+                        for (int i=0; i < len; i++) {
+                            bs.compositeData[i] = (CompositeData) compositeData[i].clone();
+                        }
+                    }
+                    break;
+
+                case ALSOBANK:
+                case ALSOSEGMENT:
+                case BANK:
+                case SEGMENT:
+                case TAGSEGMENT:
+                    // Create kid storage since we're a container type
+                    bs.children = new Vector<BaseStructure>(10);
+
+                    // Clone kids
+                    for (BaseStructure kid : children) {
+                        bs.children.add((BaseStructure)kid.clone());
+                    }
+                    break;
+
+                default:
+            }
+
+            // Do NOT clone the parent, just keep it as a reference.
+
+            return bs;
+        }
+        catch (CloneNotSupportedException e) {
+            return null;
+        }
+    }
+
+    /** Clear all existing data from a non-container structure. */
+    private void clearData() {
+
+        if (header.getDataType().isStructure()) return;
+
+        rawBytes        = null;
+        charData        = null;
+        intData         = null;
+        longData        = null;
+        shortData       = null;
+        doubleData      = null;
+        floatData       = null;
+        compositeData   = null;
+        stringData      = null;
+        stringsList     = null;
+        stringEnd       = 0;
+        numberDataItems = 0;
+    }
 
 	/**
 	 * A convenience method use instead of "instanceof" to see what type of structure we have. Note: this returns the
@@ -561,7 +700,7 @@
                     // calculate the data length so we're OK returning
                     // any reasonable value here.
                     numberDataItems = 1;
-                    if (compositeData != null) numberDataItems = compositeData.getItems().size();
+                    if (compositeData != null) numberDataItems = compositeData.length;
                     break;
                 default:
             }
@@ -744,14 +883,15 @@
 
     /**
      * This is a method from the IEvioStructure Interface. Gets the composite data as
-     * a CompositeData object, if the content type as indicated by the header is appropriate.<p>
+     * an array of CompositeData objects, if the content type as indicated by the header
+     * is appropriate.<p>
      *
-     * @return the data as a CompositeData object, or <code>null</code>
+     * @return the data as an array of CompositeData objects, or <code>null</code>
      *         if this makes no sense for the given content type.
      * @throws EvioException if the data is internally inconsistent
      */
     @Override
-    public CompositeData getCompositeData() throws EvioException {
+    public CompositeData[] getCompositeData() throws EvioException {
 
         switch (header.getDataType()) {
             case COMPOSITE:
@@ -759,7 +899,7 @@
                     if (rawBytes == null) {
                         return null;
                     }
-                    compositeData = new CompositeData(rawBytes, byteOrder);
+                    compositeData = CompositeData.parse(rawBytes, byteOrder);
                 }
                 return compositeData;
             default:
@@ -984,8 +1124,9 @@
                 }
             }
             // Look for any non-printing/control characters (not including null)
-            // and end the string there. Allow new lines (c = 10).
-            else if ( (c < 32 || c == 127) && c != 10 ) {
+            // and end the string there. Allow whitespace.
+            else if ( (c < 32 || c == 127) && !Character.isWhitespace(c)) {
+//System.out.println("  found non-printing c = " + (int)c + " at i = " + i);
                 break;
             }
         }
@@ -1051,8 +1192,8 @@
                 }
             }
             // Look for any non-printing/control characters (not including null)
-            // and end the string there. Allow new lines (c = 10).
-            else if ( (c < 32 || c == 127) && c != 10 ) {
+            // and end the string there. Allow whitespace.
+            else if ( (c < 32 || c == 127) && !Character.isWhitespace(c)) {
 //System.out.println("  found non-printing c = " + (int)c + " at i = " + i);
                 break;
             }
@@ -1166,7 +1307,9 @@
 	 */
 	@Override
 	public void remove(int index) {
-		children.remove(index);    // TODO: what if children == null ?
+        if (children == null) return;
+        BaseStructure bs = children.remove(index);
+        bs.setParent(null);
         if (children.size() < 1) isLeaf = true;
         lengthsUpToDate(false);
 	}
@@ -1178,7 +1321,9 @@
 	 */
 	@Override
 	public void remove(MutableTreeNode child) {
+        if (children == null || child == null) return;
 		children.remove(child);
+        child.setParent(null);
         if (children.size() < 1) isLeaf = true;
         lengthsUpToDate(false);
 	}
@@ -1188,7 +1333,7 @@
 	 */
 	@Override
 	public void removeFromParent() {
-		parent.remove(this);
+		if (parent != null) parent.remove(this);
 	}
 
 	/**
@@ -1199,6 +1344,92 @@
 		this.parent = (BaseStructure) parent;
 	}
 
+    /**
+     * Visit all the structures in this structure (including the structure itself --
+     * which is considered its own descendant).
+     * This is similar to listening to the event as it is being parsed,
+     * but is done to a complete (already) parsed event.
+     *
+     * @param listener an listener to notify as each structure is visited.
+     */
+    public void vistAllStructures(IEvioListener listener) {
+        visitAllDescendants(this, listener, null);
+    }
+
+    /**
+     * Visit all the structures in this structure (including the structure itself --
+     * which is considered its own descendant) in a depth first manner.
+     *
+     * @param listener an listener to notify as each structure is visited.
+     * @param filter an optional filter that must "accept" structures before
+     *               they are passed to the listener. If <code>null</code>, all
+     *               structures are passed. In this way, specific types of
+     *               structures can be captured.
+     */
+    public void vistAllStructures(IEvioListener listener, IEvioFilter filter) {
+        visitAllDescendants(this, listener, filter);
+    }
+
+    /**
+     * Visit all the descendants of a given structure
+     * (which is considered a descendant of itself.)
+     *
+     * @param structure the starting structure.
+     * @param listener an listener to notify as each structure is visited.
+     * @param filter an optional filter that must "accept" structures before
+     *               they are passed to the listener. If <code>null</code>, all
+     *               structures are passed. In this way, specific types of
+     *               structures can be captured.
+     */
+    private void visitAllDescendants(BaseStructure structure, IEvioListener listener, IEvioFilter filter) {
+        if (listener != null) {
+            boolean accept = true;
+            if (filter != null) {
+                accept = filter.accept(structure.getStructureType(), structure);
+            }
+
+            if (accept) {
+                listener.gotStructure(this, structure);
+            }
+        }
+
+        if (!(structure.isLeaf())) {
+            for (BaseStructure child : structure.getChildren()) {
+                visitAllDescendants(child, listener, filter);
+            }
+        }
+    }
+
+    /**
+     * Visit all the descendant structures, and collect those that pass a filter.
+     * @param filter the filter that must be passed. If <code>null</code>,
+     *               this will return all the structures.
+     * @return a collection of all structures that are accepted by a filter.
+     */
+    public List<BaseStructure> getMatchingStructures(IEvioFilter filter) {
+        final Vector<BaseStructure> structures = new Vector<BaseStructure>(25, 10);
+
+        IEvioListener listener = new IEvioListener() {
+            @Override
+            public void startEventParse(BaseStructure structure) { }
+
+            @Override
+            public void endEventParse(BaseStructure structure) { }
+
+            @Override
+            public void gotStructure(BaseStructure topStructure, IEvioStructure structure) {
+                structures.add((BaseStructure)structure);
+            }
+        };
+
+        vistAllStructures(listener, filter);
+
+        if (structures.size() == 0) {
+            return null;
+        }
+        return structures;
+    }
+
 	/**
 	 * This method is not relevant for this implementation.
      * An empty implementation is provided to satisfy the interface.
@@ -1225,13 +1456,18 @@
 	 * Obtain the child at the given index. Part of the <code>MutableTreeNode</code> interface.
 	 * 
 	 * @param index the target index.
-	 * @return the child at the given index.
-     * @throws ArrayIndexOutOfBoundsException if the index is out of range
-     *	       ({@code index < 0 || index >= children.size()})
+	 * @return the child at the given index or null if none
 	 */
 	@Override
 	public TreeNode getChildAt(int index) {
-		return children.elementAt(index);
+        if (children == null) return null;
+
+        BaseStructure b = null;
+        try {
+            b = children.elementAt(index);
+        }
+        catch (ArrayIndexOutOfBoundsException e) { }
+        return b;
 	}
 
 	/**
@@ -1250,10 +1486,11 @@
 	/**
 	 * Get the index of a node. Part of the <code>MutableTreeNode</code> interface.
 	 * 
-	 * @return the index of the target node.
+	 * @return the index of the target node or -1 if no such node in tree
 	 */
 	@Override
 	public int getIndex(TreeNode node) {
+        if (children == null || node == null) return -1;
 		return children.indexOf(node);
 	}
 
@@ -1269,7 +1506,6 @@
 	@Override
 	public boolean isLeaf() {
         return isLeaf;
-		//return ((children == null) || (children.size() < 1));
 	}
 	
 	/**
@@ -1441,7 +1677,9 @@
                         return;
                     }
                 }
-                compositeData.toXML(xmlWriter, this, true);
+                for (int i=0; i < compositeData.length; i++) {
+                    compositeData[i].toXML(xmlWriter, this, true);
+                }
                 break;
 
 			}
@@ -1554,7 +1792,7 @@
 	 * 
 	 * @return the length that would go in the header field (for a leaf).
 	 */
-	public int setAllHeaderLengths() {
+	public int setAllHeaderLengthsSemiOrig() throws EvioException {
         // if length info is current, don't bother to recalculate it
         if (lengthsUpToDate) {
             return header.getLength();
@@ -1579,7 +1817,7 @@
 			}
 		}
 
-        datalen += header.getHeaderLength() - 1; // extra header word for banks
+        datalen += header.getHeaderLength() - 1; // length header word
 
 		// set the datalen for the header
 		header.setLength(datalen);
@@ -1587,6 +1825,55 @@
 		return datalen;
 	}
 
+    /**
+     * Compute and set length of all header fields for this structure and all its descendants.
+     * For writing events, this will be crucial for setting the values in the headers.
+     *
+     * @return the length that would go in the header field (for a leaf).
+     * @throws EvioException if the length is too large (> {@link Integer#MAX_VALUE}),
+     */
+    public int setAllHeaderLengths() throws EvioException {
+        // if length info is current, don't bother to recalculate it
+        if (lengthsUpToDate) {
+            return header.getLength();
+        }
+
+        int datalen, len;
+
+        if (isLeaf()) {
+            // # of 32 bit ints for leaves, 0 for empty containers (also considered leaves)
+            datalen = dataLength();
+        }
+        else {
+            datalen = 0;
+
+            if (children == null) {
+System.err.println("Non leaf with null children!");
+                System.exit(1);
+            }
+            for (BaseStructure child : children) {
+                len = child.setAllHeaderLengths();
+                // Add this check to make sure structure is not being overfilled
+                if (Integer.MAX_VALUE - datalen < len) {
+                    throw new EvioException("added data overflowed containing structure");
+                }
+                datalen += len + 1;  // + 1 for the header length word of each child
+            }
+        }
+
+        len =  header.getHeaderLength() - 1;  // - 1 for length header word
+        if (Integer.MAX_VALUE - datalen < len) {
+            throw new EvioException("added data overflowed containing structure");
+        }
+
+        datalen += len;
+//System.out.println("len = " + datalen);
+        // set the datalen for the header
+        header.setLength(datalen);
+        lengthsUpToDate(true);
+        return datalen;
+    }
+
 	/**
 	 * Write myself out a byte buffer with fastest algorithms I could find.
 	 *
@@ -1595,13 +1882,15 @@
 	 */
 	public int write(ByteBuffer byteBuffer) {
 
-		int curPos, startPos = byteBuffer.position();
+        int startPos = byteBuffer.position();
 
 		// write the header
 		header.write(byteBuffer);
 
+        int curPos = byteBuffer.position();
+
 		if (isLeaf()) {
-			BaseStructureHeader header = getHeader();
+			//BaseStructureHeader header = getHeader();
 			switch (header.getDataType()) {
 			case DOUBLE64:
                 // if data sent over wire or read from file ...
@@ -1612,7 +1901,6 @@
                     }
                     // else if bytes need swapping ...
                     else {
-                        curPos = byteBuffer.position();
                         DoubleBuffer db = byteBuffer.asDoubleBuffer();
                         DoubleBuffer rawBuf = ByteBuffer.wrap(rawBytes).order(byteOrder).asDoubleBuffer();
                         db.put(rawBuf);
@@ -1621,7 +1909,6 @@
                 }
                 // else if user set data thru API (can't-rely-on / no rawBytes array) ...
 				else {
-                    curPos = byteBuffer.position();
                     DoubleBuffer db = byteBuffer.asDoubleBuffer();
                     db.put(doubleData, 0, doubleData.length);
                     byteBuffer.position(curPos + 8*doubleData.length);
@@ -1634,7 +1921,6 @@
                         byteBuffer.put(rawBytes, 0, rawBytes.length);
                     }
                     else {
-                        curPos = byteBuffer.position();
                         FloatBuffer db = byteBuffer.asFloatBuffer();
                         FloatBuffer rawBuf = ByteBuffer.wrap(rawBytes).order(byteOrder).asFloatBuffer();
                         db.put(rawBuf);
@@ -1642,7 +1928,6 @@
                     }
                 }
 				else {
-                    curPos = byteBuffer.position();
                     FloatBuffer db = byteBuffer.asFloatBuffer();
                     db.put(floatData, 0, floatData.length);
                     byteBuffer.position(curPos + 4*floatData.length);
@@ -1656,7 +1941,6 @@
                         byteBuffer.put(rawBytes, 0, rawBytes.length);
                     }
                     else {
-                        curPos = byteBuffer.position();
                         LongBuffer db = byteBuffer.asLongBuffer();
                         LongBuffer rawBuf = ByteBuffer.wrap(rawBytes).order(byteOrder).asLongBuffer();
                         db.put(rawBuf);
@@ -1664,7 +1948,6 @@
                     }
                 }
 				else {
-                    curPos = byteBuffer.position();
                     LongBuffer db = byteBuffer.asLongBuffer();
                     db.put(longData, 0, longData.length);
                     byteBuffer.position(curPos + 8*longData.length);
@@ -1679,7 +1962,6 @@
                         byteBuffer.put(rawBytes, 0, rawBytes.length);
                     }
                     else {
-                        curPos = byteBuffer.position();
                         IntBuffer db = byteBuffer.asIntBuffer();
                         IntBuffer rawBuf = ByteBuffer.wrap(rawBytes).order(byteOrder).asIntBuffer();
                         db.put(rawBuf);
@@ -1687,7 +1969,6 @@
                     }
                 }
 				else {
-                    curPos = byteBuffer.position();
                     IntBuffer db = byteBuffer.asIntBuffer();
                     db.put(intData, 0, intData.length);
                     byteBuffer.position(curPos + 4*intData.length);
@@ -1701,7 +1982,6 @@
                         byteBuffer.put(rawBytes, 0, rawBytes.length);
                     }
                     else {
-                        curPos = byteBuffer.position();
                         ShortBuffer db = byteBuffer.asShortBuffer();
                         ShortBuffer rawBuf = ByteBuffer.wrap(rawBytes).order(byteOrder).asShortBuffer();
                         db.put(rawBuf);
@@ -1709,7 +1989,6 @@
                     }
                 }
 				else {
-                    curPos = byteBuffer.position();
                     ShortBuffer db = byteBuffer.asShortBuffer();
                     db.put(shortData, 0, shortData.length);
                     byteBuffer.position(curPos + 2*shortData.length);
@@ -1747,6 +2026,11 @@
                         byteBuffer.put(rawBytes, 0, rawBytes.length);
                     }
                     else {
+//                        System.out.println("Before swap in writing output:");
+//                        int[] intA = ByteDataTransformer.getAsIntArray(rawBytes, ByteOrder.BIG_ENDIAN);
+//                        for (int i : intA) {
+//                            System.out.println("Ox" + Integer.toHexString(i));
+//                        }
                         // swap rawBytes
                         byte[] swappedRaw = new byte[rawBytes.length];
                         try {
@@ -1756,6 +2040,11 @@
                         catch (EvioException e) { /* never happen */ }
                         // write them to buffer
                         byteBuffer.put(swappedRaw, 0, swappedRaw.length);
+//                        System.out.println("Swap in writing output:");
+//                        intA = ByteDataTransformer.getAsIntArray(swappedRaw, ByteOrder.LITTLE_ENDIAN);
+//                        for (int i : intA) {
+//                            System.out.println("Ox" + Integer.toHexString(i));
+//                        }
                     }
                 }
 				break;
@@ -1764,7 +2053,7 @@
 			} // switch
 
 		} // isLeaf
-		else {
+		else if (children != null) {
 			for (BaseStructure child : children) {
 				child.write(byteBuffer);
 			}
@@ -1773,36 +2062,48 @@
 		return byteBuffer.position() - startPos;
 	}
 
-	/**
-	 * Appends int data to the structure. If the structure has no data, then this
-	 * is the same as setting the data.
-	 * @param data the int data to append, or set if there is no existing data.
-	 * @throws EvioException
-	 */
-	public void appendIntData(int data[]) throws EvioException {
-		
-		// make sure the structure is set to hold this kind of data
-		DataType dataType = header.getDataType();
-		if ((dataType != DataType.INT32) && (dataType != DataType.UINT32)) {
-			throw new EvioException("Tried to append int data to a structure of type: " + dataType);
-		}
-		
-		// if no data to append, just cave
-		if (data == null) {
-			return;
-		}
-		
-		// if no int data ...
-		if (intData == null) {
-		    // if no raw data, things are easy
+
+    //----------------------------------------------------------------------
+    // Methods to append to exising data if any or to set the data if none.
+    //----------------------------------------------------------------------
+
+
+    /**
+     * Appends int data to the structure. If the structure has no data, then this
+     * is the same as setting the data.
+     * @param data the int data to append, or set if there is no existing data.
+     * @throws EvioException if adding data to a structure of a different data type;
+     *                       if data has too many elements to store in raw byte array (JVM limit)
+     */
+    public void appendIntData(int data[]) throws EvioException {
+
+        // make sure the structure is set to hold this kind of data
+        DataType dataType = header.getDataType();
+        if ((dataType != DataType.INT32) && (dataType != DataType.UINT32)) {
+            throw new EvioException("Tried to append int data to a structure of type: " + dataType);
+        }
+
+        // if no data to append, just cave
+        if (data == null) {
+            return;
+        }
+
+        // if no int data ...
+        if (intData == null) {
+            // if no raw data, things are easy
             if (rawBytes == null) {
-			    intData = data;
+                intData = data;
                 numberDataItems = data.length;
             }
             // otherwise expand raw data first, then add int array
             else {
                 int size1 = rawBytes.length/4;
                 int size2 = data.length;
+
+                // TODO: Will need to revise this if using Java 9+ with long array indeces
+                if (Integer.MAX_VALUE - size1 < size2) {
+                    throw new EvioException("added data overflowed containing structure");
+                }
                 intData = new int[size1 + size2];
                 // unpack existing raw data
                 ByteDataTransformer.toIntArray(rawBytes, byteOrder, intData, 0);
@@ -1810,29 +2111,36 @@
                 System.arraycopy(data, 0, intData, size1, size2);
                 numberDataItems = size1 + size2;
             }
-		}
-		else {
-			int size1 = intData.length; // existing data
-			int size2 = data.length;    // new data to append
-			intData = Arrays.copyOf(intData, size1 + size2);  // extend existing array
-			System.arraycopy(data, 0, intData, size1, size2); // append
+        }
+        else {
+            int size1 = intData.length; // existing data
+            int size2 = data.length;    // new data to append
+
+            // TODO: Will need to revise this if using Java 9+ with long array indeces
+            if (Integer.MAX_VALUE - size1 < size2) {
+                throw new EvioException("added data overflowed containing structure");
+            }
+            intData = Arrays.copyOf(intData, size1 + size2);  // extend existing array
+            System.arraycopy(data, 0, intData, size1, size2); // append
             numberDataItems += size2;
-		}
+        }
 
         // This is not necessary but results in a tremendous performance
         // boost (10x) when writing events over a high speed network. Allows
         // the write to be a byte array copy.
+        // Storing data as raw bytes limits the # of elements to Integer.MAX_VALUE/4.
         rawBytes = ByteDataTransformer.toBytes(intData, byteOrder);
 
         lengthsUpToDate(false);
-		setAllHeaderLengths();
-	}
-	
+        setAllHeaderLengths();
+    }
+
 	/**
 	 * Appends short data to the structure. If the structure has no data, then this
 	 * is the same as setting the data.
 	 * @param data the short data to append, or set if there is no existing data.
-	 * @throws EvioException
+     * @throws EvioException if adding data to a structure of a different data type;
+     *                       if data has too many elements to store in raw byte array (JVM limit)
 	 */
 	public void appendShortData(short data[]) throws EvioException {
 		
@@ -1853,6 +2161,10 @@
             else {
                 int size1 = (rawBytes.length - header.getPadding())/2;
                 int size2 = data.length;
+
+                if (Integer.MAX_VALUE - size1 < size2) {
+                    throw new EvioException("added data overflowed containing structure");
+                }
                 shortData = new short[size1 + size2];
                 ByteDataTransformer.toShortArray(rawBytes, header.getPadding(),
                                                  byteOrder, shortData, 0);
@@ -1863,6 +2175,10 @@
 		else {
 			int size1 = shortData.length;
 			int size2 = data.length;
+
+            if (Integer.MAX_VALUE - size1 < size2) {
+                throw new EvioException("added data overflowed containing structure");
+            }
 			shortData = Arrays.copyOf(shortData, size1 + size2);
 			System.arraycopy(data, 0, shortData, size1, size2);
             numberDataItems += size2;
@@ -1872,6 +2188,10 @@
         if (numberDataItems%2 != 0) {
             header.setPadding(2);
 
+            if (Integer.MAX_VALUE - 2*numberDataItems < 2) {
+                throw new EvioException("added data overflowed containing structure");
+            }
+
             // raw bytes must include padding
             rawBytes = new byte[2*numberDataItems + 2];
             ByteDataTransformer.toBytes(shortData, byteOrder, rawBytes, 0);
@@ -1890,7 +2210,8 @@
 	 * Appends long data to the structure. If the structure has no data, then this
 	 * is the same as setting the data.
 	 * @param data the long data to append, or set if there is no existing data.
-	 * @throws EvioException
+     * @throws EvioException if adding data to a structure of a different data type;
+     *                       if data has too many elements to store in raw byte array (JVM limit)
 	 */
 	public void appendLongData(long data[]) throws EvioException {
 		
@@ -1911,6 +2232,10 @@
             else {
                 int size1 = rawBytes.length/8;
                 int size2 = data.length;
+
+                if (Integer.MAX_VALUE - size1 < size2) {
+                    throw new EvioException("added data overflowed containing structure");
+                }
                 longData = new long[size1 + size2];
                 ByteDataTransformer.toLongArray(rawBytes, byteOrder, longData, 0);
                 System.arraycopy(data, 0, longData, size1, size2);
@@ -1920,6 +2245,10 @@
 		else {
 			int size1 = longData.length;
 			int size2 = data.length;
+
+            if (Integer.MAX_VALUE - size1 < size2) {
+                throw new EvioException("added data overflowed containing structure");
+            }
 			longData = Arrays.copyOf(longData, size1 + size2);
 			System.arraycopy(data, 0, longData, size1, size2);
             numberDataItems += size2;
@@ -1935,7 +2264,8 @@
 	 * Appends byte data to the structure. If the structure has no data, then this
 	 * is the same as setting the data.
 	 * @param data the byte data to append, or set if there is no existing data.
-	 * @throws EvioException
+     * @throws EvioException if adding data to a structure of a different data type;
+     *                       if data has too many elements to store in raw byte array (JVM limit)
 	 */
 	public void appendByteData(byte data[]) throws EvioException {
 		
@@ -1956,6 +2286,10 @@
             else {
                 int size1 = rawBytes.length - header.getPadding();
                 int size2 = data.length;
+
+                if (Integer.MAX_VALUE - size1 < size2) {
+                    throw new EvioException("added data overflowed containing structure");
+                }
                 charData = new byte[size1 + size2];
                 System.arraycopy(rawBytes, 0, charData, 0, size1);
                 System.arraycopy(data, 0, charData, size1, size2);
@@ -1965,6 +2299,10 @@
 		else {
 			int size1 = charData.length;
 			int size2 = data.length;
+
+            if (Integer.MAX_VALUE - size1 < size2) {
+                throw new EvioException("added data overflowed containing structure");
+            }
 			charData = Arrays.copyOf(charData, size1 + size2);
 			System.arraycopy(data, 0, charData, size1, size2);
             numberDataItems += data.length;
@@ -1973,10 +2311,15 @@
         // store necessary padding to 4 byte boundaries.
         int padding = padCount[numberDataItems%4];
         header.setPadding(padding);
+//System.out.println("# data items = " + numberDataItems + ", padding = " + padding);
 
+        // Array creation sets everything to zero. Only need to copy in data.
+        if (Integer.MAX_VALUE - numberDataItems < padding) {
+            throw new EvioException("added data overflowed containing structure");
+        }
         rawBytes = new byte[numberDataItems + padding];
         System.arraycopy(charData,  0, rawBytes, 0, numberDataItems);
-        System.arraycopy(padValues, 0, rawBytes, 0, padding);
+//        System.arraycopy(padValues, 0, rawBytes, numberDataItems, padding); // unnecessary
 
         lengthsUpToDate(false);
 		setAllHeaderLengths();
@@ -1986,7 +2329,8 @@
 	 * Appends float data to the structure. If the structure has no data, then this
 	 * is the same as setting the data.
 	 * @param data the float data to append, or set if there is no existing data.
-	 * @throws EvioException
+     * @throws EvioException if adding data to a structure of a different data type;
+     *                       if data has too many elements to store in raw byte array (JVM limit)
 	 */
 	public void appendFloatData(float data[]) throws EvioException {
 		
@@ -2007,6 +2351,10 @@
             else {
                 int size1 = rawBytes.length/4;
                 int size2 = data.length;
+
+                if (Integer.MAX_VALUE - size1 < size2) {
+                    throw new EvioException("added data overflowed containing structure");
+                }
                 floatData = new float[size1 + size2];
                 ByteDataTransformer.toFloatArray(rawBytes, byteOrder, floatData, 0);
                 System.arraycopy(data, 0, floatData, size1, size2);
@@ -2016,6 +2364,10 @@
 		else {
 			int size1 = floatData.length;
 			int size2 = data.length;
+
+            if (Integer.MAX_VALUE - size1 < size2) {
+                throw new EvioException("added data overflowed containing structure");
+            }
 			floatData = Arrays.copyOf(floatData, size1 + size2);
 			System.arraycopy(data, 0, floatData, size1, size2);
             numberDataItems += data.length;
@@ -2029,9 +2381,10 @@
 
     /**
      * Appends string to the structure (as ascii). If the structure has no data, then this
-     * is the same as setting the data.
+     * is the same as setting the data. Don't worry about checking for size limits since
+     * jevio structures will never contain a char array > {@link Integer#MAX_VALUE} in size.
      * @param s the string to append (as ascii), or set if there is no existing data.
-     * @throws EvioException
+     * @throws EvioException if adding data to a structure of a different data type
      */
     public void appendStringData(String s) throws EvioException {
 
@@ -2114,7 +2467,8 @@
 	 * Appends double data to the structure. If the structure has no data, then this
 	 * is the same as setting the data.
 	 * @param data the double data to append, or set if there is no existing data.
-	 * @throws EvioException
+     * @throws EvioException if adding data to a structure of a different data type;
+     *                       if data has too many elements to store in raw byte array (JVM limit)
 	 */
 	public void appendDoubleData(double data[]) throws EvioException {
 		
@@ -2135,6 +2489,10 @@
             else {
                 int size1 = rawBytes.length/8;
                 int size2 = data.length;
+
+                if (Integer.MAX_VALUE - size1 < size2) {
+                    throw new EvioException("added data overflowed containing structure");
+                }
                 doubleData = new double[size1 + size2];
                 ByteDataTransformer.toDoubleArray(rawBytes, byteOrder, doubleData, 0);
                 System.arraycopy(data, 0, doubleData, size1, size2);
@@ -2144,6 +2502,10 @@
 		else {
 			int size1 = doubleData.length;
 			int size2 = data.length;
+
+            if (Integer.MAX_VALUE - size1 < size2) {
+                throw new EvioException("added data overflowed containing structure");
+            }
 			doubleData = Arrays.copyOf(doubleData, size1 + size2);
 			System.arraycopy(data, 0, doubleData, size1, size2);
             numberDataItems += data.length;
@@ -2155,32 +2517,256 @@
 		setAllHeaderLengths();
 	}
 
+
     /**
-	 * Sets the CompositeData object of this structure. No "append" mode for
-     * this type of data exists.
-     *
-	 * @param data CompositeData object
-	 * @throws EvioException
+     * Appends CompositeData objects to the structure. If the structure has no data, then this
+     * is the same as setting the data.
+     * @param data the CompositeData objects to append, or set if there is no existing data.
+     * @throws EvioException if adding data to a structure of a different data type;
+     *                       if data takes up too much memory to store in raw byte array (JVM limit)
 	 */
-	public void appendCompositeData(CompositeData data) throws EvioException {
+	public void appendCompositeData(CompositeData data[]) throws EvioException {
 
 		DataType dataType = header.getDataType();
[truncated at 1000 lines; 247 more skipped]

jevio-base/src/main/java/org/jlab/coda/jevio/test
Tester.java 1.2 -> 1.3
diff -u -r1.2 -r1.3
--- Tester.java	20 Mar 2012 23:21:49 -0000	1.2
+++ Tester.java	3 Apr 2013 20:07:47 -0000	1.3
@@ -2,6 +2,7 @@
 
 import org.jlab.coda.jevio.*;
 
+import javax.swing.tree.DefaultTreeModel;
 import java.io.File;
 import java.io.IOException;
 import java.nio.ByteBuffer;
@@ -39,7 +40,41 @@
         System.out.println();
     }
 
+    // Test to see when code chokes adding data to a bank
     public static void main(String args[]) {
+        try {
+            EvioEvent bank = new EvioEvent(1, DataType.BANK,  1);
+            EvioBank ibank = new EvioBank (2, DataType.INT32, 2);
+            EvioBank bbank = new EvioBank (3, DataType.INT32, 3);
+            EvioBank lbank = new EvioBank (4, DataType.LONG64, 4);
+            //int[] data     = new int[2500000];
+           // int[] data3    = new int[2147483646];
+            int[] data4    = new int[1];
+            int[] data2    = new int[1];
+            long[] data5    = new long[268435456];
+            // Keep adding to event
+            EventBuilder builder = new EventBuilder(bank);
+            builder.appendLongData(lbank, data5);
+            //builder.appendIntData(bbank, data3);
+            //builder.appendIntData(bbank, data4);
+            //ibank.appendIntData();
+            //ibank.setIntData(data2);
+            for (int i=0; i < 2000; i++) {
+                System.out.print(i + " ");
+                    builder.addChild(bank, ibank);
+            }
+           // builder.appendIntData(ibank, data);
+            //ibank.appendIntData(data);
+
+        }
+        catch (EvioException e) {
+            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
+        }
+
+
+    }
+
+    public static void main55(String args[]) {
         byte[] data = new byte[] {(byte)1, (byte)2};
         int off=0;
         // treat the high bit improperly (shift first, then mask, high byte disappears)
@@ -51,6 +86,78 @@
         System.out.println("littleE s1 = 0x"+ i1 + ", s2 = 0x" + i2);
     }
 
+    public static void main33(String args[]) {
+        EvioEvent ev   = new EvioEvent(1, DataType.INT32,  1);
+        EvioBank bank  = new EvioBank (2, DataType.BANK,  2);
+        EvioBank ibank = new EvioBank (3, DataType.INT32, 3);
+        int[] data     = new int[] {1,2,3,4,5};
+
+        // Build a bigger event tree
+//        EventBuilder builder = new EventBuilder(ev);
+//        try {
+//            builder.addChild(ev, bank);
+//            builder.addChild(bank, ibank);
+//        }
+//        catch (EvioException e) {}
+//        ibank.setIntData(data);
+
+        try {
+            ev.setDictionaryXML("blah blah blah");
+            ev.appendIntData(data);
+        }
+        catch (EvioException e) {}
+        System.out.println("Now create the clone ...");
+
+        EvioEvent evClone = (EvioEvent)ev.clone();
+//        EvioBank  bkClone = (EvioBank)bank.clone();
+
+//        DefaultTreeModel treeModel = ev.getTreeModel();
+//        System.out.println("Tree model object for original event:\n" + treeModel);
+//        System.out.println("Tree for original event:\n" + treeModel.toString());
+
+        int[] data1 = evClone.getIntData();
+//        int[] data2 = bkClone.getIntData();
+
+        if (data1 != null) {
+            for (int i : data1) {
+                System.out.println("event i = " + i);
+            }
+        }
+        else {
+            System.out.println("event int data is NULL !!!");
+        }
+
+        String dict = evClone.getDictionaryXML();
+        if (dict != null) {
+            System.out.println("dictionary = \n" + dict);
+        }
+        else {
+            System.out.println("event dictionary is NULL !!!");
+        }
+
+
+        //int len = ((BankHeader)evClone.getHeader()).getHeaderLength();
+        //int len = evClone.getHeader().getHeaderLength();
+        int len = ev.getHeader().getHeaderLength();
+        System.out.println("header length = " + len);
+
+        System.out.println("Change ev tag from 1 to 66");
+        ev.getHeader().setTag(66);
+        System.out.println("\nev header = " + ev.getHeader().toString());
+        System.out.println("\nclone header = " + evClone.getHeader().toString());
+
+
+
+//        if (data2 != null) {
+//            for (int i : data2) {
+//                System.out.println("bank i = " + i);
+//            }
+//        }
+//        else {
+//            System.out.println("bank int data is NULL !!!");
+//        }
+    }
+
 
     /** For testing only */
     public static void main2(String args[]) {
@@ -67,8 +174,15 @@
                 doubleData[i] = i;
             }
 
-            byte[] rawBytesBE = ByteDataTransformer.toBytes(doubleData, ByteOrder.BIG_ENDIAN);
-            byte[] rawBytesLE = ByteDataTransformer.toBytes(doubleData, ByteOrder.LITTLE_ENDIAN);
+            byte[] rawBytesBE = new byte[0];
+            byte[] rawBytesLE = new byte[0];
+            try {
+                rawBytesBE = ByteDataTransformer.toBytes(doubleData, ByteOrder.BIG_ENDIAN);
+                rawBytesLE = ByteDataTransformer.toBytes(doubleData, ByteOrder.LITTLE_ENDIAN);
+            }
+            catch (EvioException e) {
+                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
+            }
             byte[] rawBytes = rawBytesBE;
 
             ByteBuffer byteBufferBE = ByteBuffer.allocate(1024);
@@ -173,17 +287,23 @@
             //printDoubleBuffer(byteBuffer);
 
             // option 7
-            t1 = System.currentTimeMillis();
-            for (int j=0; j < LOOPS; j++) {
-                byteBuffer.clear();
-                byte[] bytes = ByteDataTransformer.toBytes(doubleData, byteBuffer.order());
-                byteBuffer.put(bytes);
+
+            try {
+                t1 = System.currentTimeMillis();
+                for (int j=0; j < LOOPS; j++) {
+                    byteBuffer.clear();
+                    byte[] bytes = ByteDataTransformer.toBytes(doubleData, byteBuffer.order());
+                    byteBuffer.put(bytes);
+                }
+                t2 = System.currentTimeMillis();
+                deltaT = t2 - t1;
+                freq = (double) LOOPS / deltaT * 1000;
+                System.out.printf("loopRate D7: %2.3g Hz,  time = %2.3g sec\n", freq, (deltaT/1000.));
+                //printDoubleBuffer(byteBuffer);
+            }
+            catch (EvioException e) {
+                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
             }
-            t2 = System.currentTimeMillis();
-            deltaT = t2 - t1;
-            freq = (double) LOOPS / deltaT * 1000;
-            System.out.printf("loopRate D7: %2.3g Hz,  time = %2.3g sec\n", freq, (deltaT/1000.));
-            //printDoubleBuffer(byteBuffer);
 
             // option 8
 
@@ -235,8 +355,15 @@
                 intData[i] = i;
             }
 
-            byte[] rawBytesIBE = ByteDataTransformer.toBytes(intData, ByteOrder.BIG_ENDIAN);
-            byte[] rawBytesILE = ByteDataTransformer.toBytes(intData, ByteOrder.LITTLE_ENDIAN);
+            byte[] rawBytesIBE = new byte[0];
+            byte[] rawBytesILE = new byte[0];
+            try {
+                rawBytesIBE = ByteDataTransformer.toBytes(intData, ByteOrder.BIG_ENDIAN);
+                rawBytesILE = ByteDataTransformer.toBytes(intData, ByteOrder.LITTLE_ENDIAN);
+            }
+            catch (EvioException e) {
+                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
+            }
             byte[] rawBytesI = rawBytesIBE;
 
             ByteBuffer byteBufferIBE = ByteBuffer.allocate(1024);
@@ -341,17 +468,23 @@
             //printIntBuffer(byteBufferI);
 
             // option 7
-            t1 = System.currentTimeMillis();
-            for (int j=0; j < LOOPS; j++) {
-                byteBufferI.clear();
-                byte[] bytes = ByteDataTransformer.toBytes(intData, byteBufferI.order());
-                byteBufferI.put(bytes);
+
+            try {
+                t1 = System.currentTimeMillis();
+                for (int j=0; j < LOOPS; j++) {
+                    byteBufferI.clear();
+                    byte[] bytes = ByteDataTransformer.toBytes(intData, byteBufferI.order());
+                    byteBufferI.put(bytes);
+                }
+                t2 = System.currentTimeMillis();
+                deltaT = t2 - t1;
+                freq = (double) LOOPS / deltaT * 1000;
+                System.out.printf("loopRate I7: %2.3g Hz,  time = %2.3g sec\n", freq, (deltaT/1000.));
+                //printIntBuffer(byteBufferI);
+            }
+            catch (EvioException e) {
+                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
             }
-            t2 = System.currentTimeMillis();
-            deltaT = t2 - t1;
-            freq = (double) LOOPS / deltaT * 1000;
-            System.out.printf("loopRate I7: %2.3g Hz,  time = %2.3g sec\n", freq, (deltaT/1000.));
-            //printIntBuffer(byteBufferI);
 
             // option 8  (really slow)
 //            t1 = System.currentTimeMillis();
@@ -612,8 +745,15 @@
                 doubleData[i] = i;
             }
 
-            byte[] rawBytesIBE = ByteDataTransformer.toBytes(intData, ByteOrder.BIG_ENDIAN);
-            byte[] rawBytesILE = ByteDataTransformer.toBytes(intData, ByteOrder.LITTLE_ENDIAN);
+            byte[] rawBytesIBE = new byte[0];
+            byte[] rawBytesILE = new byte[0];
+            try {
+                rawBytesIBE = ByteDataTransformer.toBytes(intData, ByteOrder.BIG_ENDIAN);
+                rawBytesILE = ByteDataTransformer.toBytes(intData, ByteOrder.LITTLE_ENDIAN);
+            }
+            catch (EvioException e) {
+                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
+            }
             byte[] rawBytesI = rawBytesILE;
 
 

jevio-base/src/main/java/org/jlab/coda/jevio/test
FileTest.java 1.1 -> 1.2
diff -u -r1.1 -r1.2
--- FileTest.java	28 Feb 2012 19:41:37 -0000	1.1
+++ FileTest.java	3 Apr 2013 20:07:47 -0000	1.2
@@ -2,10 +2,11 @@
 
 import org.jlab.coda.jevio.*;
 
-import java.io.File;
-import java.io.IOException;
+import java.io.*;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
+import java.nio.channels.FileChannel;
+import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -15,217 +16,200 @@
  */
 public class FileTest {
 
-     /** For testing only */
-     public static void main0(String args[]) {
 
-         //create an event writer to write out the test events.
-         String fileName  = "../../testdata/BigOut3.ev";
-         File file = new File(fileName);
-
-         ByteBuffer myBuf = ByteBuffer.allocate(2000);
-         myBuf.order(ByteOrder.LITTLE_ENDIAN);
-
-         // xml dictionary
-         String dictionary =
-                 "<xmlDict>\n" +
-                         "  <xmldumpDictEntry name=\"bank of segs\"   tag=\"1\"   num=\"1\"/>\n" +
-                         "  <xmldumpDictEntry name=\"seg of banks\"   tag=\"2\"   />\n" +
-                         "  <xmldumpDictEntry name=\"shorts pad0\"    tag=\"3\"   num=\"1.3\"/>\n" +
-                         "  <xmldumpDictEntry name=\"shorts pad2\"    tag=\"4\"   num=\"1.4\"/>\n" +
-                         "  <xmldumpDictEntry name=\"bank of chars\"  tag=\"5\"   num=\"5\"/>\n" +
-                         "  <xmldumpDictEntry name=\"chars pad0\"     tag=\"6\"   num=\"5.6\"/>\n" +
-                         "  <xmldumpDictEntry name=\"chars pad3\"     tag=\"7\"   num=\"5.7\"/>\n" +
-                         "  <xmldumpDictEntry name=\"chars pad2\"     tag=\"8\"   num=\"5.8\"/>\n" +
-                         "  <xmldumpDictEntry name=\"chars pad1\"     tag=\"9\"   num=\"5.9\"/>\n" +
-                 "</xmlDict>";
-
-         // data
-         byte[]  byteData1   = new byte[]  {1,2,3,4};
-         byte[]  byteData2   = new byte[]  {1,2,3,4,5};
-         byte[]  byteData3   = new byte[]  {1,2,3,4,5,6};
-         byte[]  byteData4   = new byte[]  {1,2,3,4,5,6,7};
-         short[] shortData1  = new short[] {11,22};
-         short[] shortData2  = new short[] {11,22,33};
 
-         try {
-             EventWriter eventWriterNew = new EventWriter(file, 100, 3,
-                                                          ByteOrder.BIG_ENDIAN, dictionary, null);
-
-             // event - bank of banks
-             EventBuilder eventBuilder2 = new EventBuilder(1, DataType.SEGMENT, 1);
-             EvioEvent eventShort = eventBuilder2.getEvent();
-
-             // bank of short banks
-             EvioSegment segBanks = new EvioSegment(2, DataType.BANK);
-
-             // 3 shorts
-             EvioBank shortBank1 = new EvioBank(3, DataType.SHORT16, 3);
-             shortBank1.appendShortData(shortData1);
-             eventBuilder2.addChild(segBanks, shortBank1);
-
-             EvioBank shortBank2 = new EvioBank(4, DataType.SHORT16, 4);
-             shortBank2.appendShortData(shortData2);
-             eventBuilder2.addChild(segBanks, shortBank2);
-
-             eventBuilder2.addChild(eventShort, segBanks);
-             eventWriterNew.writeEvent(eventShort);
-
-
-
-             // each event is a trivial event containing an array of ints - all zeros
-             EventBuilder eventBuilder = new EventBuilder(5, DataType.BANK, 5);
-             EvioEvent event = eventBuilder.getEvent();
-
-             // event 1
-             EvioBank charBank1 = new EvioBank(6, DataType.CHAR8, 6);
-             charBank1.appendByteData(byteData1);
-             eventBuilder.addChild(event, charBank1);
-
-             // event 2
-             EvioBank charBank2 = new EvioBank(7, DataType.CHAR8, 7);
-             charBank2.appendByteData(byteData2);
-             eventBuilder.addChild(event, charBank2);
-
-             // event 3
-             EvioBank charBank3 = new EvioBank(8, DataType.CHAR8, 8);
-             charBank3.appendByteData(byteData3);
-             eventBuilder.addChild(event, charBank3);
-
-             // event 4
-             EvioBank charBank4 = new EvioBank(9, DataType.CHAR8, 9);
-             charBank4.appendByteData(byteData4);
-             eventBuilder.addChild(event, charBank4);
+    /** For testing the speed difference in writeEvent algorithms.
+     *  Want about 1000 little events/block.  */
+    public static void main11(String args[]) {
 
-             eventWriterNew.writeEvent(event);
+        // Create an event writer to write out the test events.
+        String fileName  = "/daqfs/home/timmer/coda/jevio-4.0/testdata/speedTest.ev";
+        File file = new File(fileName);
 
-             // all done writing
-             eventWriterNew.close();
-         }
-         catch (IOException e) {
-             e.printStackTrace();
-         }
-         catch (EvioException e) {
-             e.printStackTrace();
-         }
+        // data
+        byte[] byteData1 = new byte[499990];
 
+        int num, count = 0;
+        long t1=0, t2=0, time, totalT=0, totalCount=0;
+        double rate, avgRate;
 
-         File fileIn = new File(fileName);
-         System.out.println("read ev file: " + fileName + " size: " + fileIn.length());
+        try {
+            // 1MB max block size, 2 max # events/block
+            EventWriter eventWriter = new EventWriter(file, 1000000, 2,
+                                                      ByteOrder.BIG_ENDIAN, null, null);
+
+            // event -> bank of bytes
+            // each event (including header) is 100 bytes
+            EventBuilder eventBuilder = new EventBuilder(1, DataType.CHAR8, 1);
+            EvioEvent ev = eventBuilder.getEvent();
+            ev.appendByteData(byteData1);
+
+            // keep track of time
+            t1 = System.currentTimeMillis();
+
+
+            for (int j=0; j < 10; j++) {
+// 10 MB file with 10 block headers
+                for (int i=0; i < 2; i++) {
+                    eventWriter.writeEvent(ev);
+                    count++;
+                }
+            }
 
-         try {
-             EvioReader evioReader = new EvioReader(fileName);
-             EvioEvent ev = evioReader.parseNextEvent();
-             List<BaseStructure> l = StructureFinder.getMatchingStructures(ev,"seg of banks",
-                                                              NameProviderFactory.createNameProvider(dictionary));
+            // all done writing
+            eventWriter.close();
 
+            // calculate the event rate
+            t2 = System.currentTimeMillis();
+            time = t2 - t1;
+            rate = 1000.0 * ((double) count) / time;
+            totalCount += count;
+            totalT += time;
+            avgRate = 1000.0 * ((double) totalCount) / totalT;
+            System.out.println("rate = " + String.format("%.3g", rate) +
+                                       " Hz,  avg = " + String.format("%.3g", avgRate));
+            System.out.println("time = " + (time) + " milliseconds");
+            count = 0;
+            t1 = System.currentTimeMillis();
+        }
+        catch (IOException e) {
+            e.printStackTrace();
+        }
+        catch (EvioException e) {
+            e.printStackTrace();
+        }
 
-             if (l != null) {
-                 for (BaseStructure str : l) {
-                     System.out.println("Found bank: tag = " + str.getHeader().getTag() +
-                     ", num = " + str.getHeader().getNumber());
-                 }
-             }
-             else {
-                 System.out.println("Found NO banks dude");
-             }
 
-         }
-         catch (EvioException e) {
-             e.printStackTrace();
-         }
-         catch (IOException e) {
-             e.printStackTrace();
-         }
-     }
+    }
 
 
-    /** For testing only */
-    public static void main7(String args[]) {
+    /** For WRITING a local file. */
+    public static void main12(String args[]) {
 
-        //create an event writer to write out the test events.
-        String fileName  = "../../testdata/BigOut3.ev";
+        // String fileName  = "./myData.ev";
+        String fileName  = "/daqfs/home/timmer/coda/jevio-4.0/testdata/fileTest.ev";
         File file = new File(fileName);
-
-        ByteBuffer myBuf = ByteBuffer.allocate(10000);
-        myBuf.order(ByteOrder.LITTLE_ENDIAN);
+        ByteBuffer myBuf = null;
 
         // xml dictionary
-        String dictionary =
-                "<xmlDict>\n" +
-                        "  <xmldumpDictEntry name=\"bank\"           tag=\"1\"   num=\"1\"/>\n" +
-                        "  <xmldumpDictEntry name=\"bank of shorts\" tag=\"2\"   num=\"1.2\"/>\n" +
-                        "  <xmldumpDictEntry name=\"shorts pad0\"    tag=\"3\"   num=\"1.2.3\"/>\n" +
-                        "  <xmldumpDictEntry name=\"shorts pad2\"    tag=\"4\"   num=\"1.2.4\"/>\n" +
-                        "  <xmldumpDictEntry name=\"bank of chars\"  tag=\"5\"   num=\"5\"/>\n" +
-                        "  <xmldumpDictEntry name=\"chars pad0\"     tag=\"6\"   num=\"5.6\"/>\n" +
-                        "  <xmldumpDictEntry name=\"chars pad3\"     tag=\"7\"   num=\"5.7\"/>\n" +
-                        "  <xmldumpDictEntry name=\"chars pad2\"     tag=\"8\"   num=\"5.8\"/>\n" +
-                        "  <xmldumpDictEntry name=\"chars pad1\"     tag=\"9\"   num=\"5.9\"/>\n" +
-                "</xmlDict>";
+        String xmlDictionary =
+            "<xmlDict>\n" +
+            "  <bank name=\"bank of banks\"          tag=\"1\"   num=\"1\">\n" +
+            "     <bank name=\"bank of segments\"    tag=\"2\"   num=\"2\">\n" +
+            "       <leaf name=\"segment of shorts\" tag=\"3\"   />\n" +
+            "     </bank>\n" +
+            "     <bank name=\"bank of banks\"       tag=\"4\"   num=\"4\">\n" +
+            "       <leaf name=\"bank of chars\"     tag=\"5\"   num=\"5\"/>\n" +
+            "     </bank>\n" +
+            "  </bank>\n" +
+            "  <dictEntry name=\"last bank\"        tag=\"33\"  num=\"66\"/>\n" +
+            "</xmlDict>";
 
         // data
-        byte[]  byteData1   = new byte[]  {1,2,3,4};
-        byte[]  byteData2   = new byte[]  {1,2,3,4,5};
-        byte[]  byteData3   = new byte[]  {1,2,3,4,5,6};
-        byte[]  byteData4   = new byte[]  {1,2,3,4,5,6,7};
-        short[] shortData1  = new short[] {11,22};
-        short[] shortData2  = new short[] {11,22,33};
-
-        try {
-            EventWriter eventWriterNew = new EventWriter(file, 100, 3,
-                                                         ByteOrder.BIG_ENDIAN, dictionary, null);
-
-            // event - bank of banks
-            EventBuilder eventBuilder2 = new EventBuilder(1, DataType.BANK, 1);
-            EvioEvent eventShort = eventBuilder2.getEvent();
+        byte[]  byteData1 = new byte[]  {1,2,3,4,5};
+        int[]   intData1  = new int[]   {4,5,6};
+        int[]   intData2  = new int[]   {7,8,9};
+        short[] shortData = new short[] {11,22,33};
 
-            // bank of short banks
-            EvioBank bankBanks = new EvioBank(2, DataType.BANK, 2);
-
-            // 3 shorts
-            EvioBank shortBank1 = new EvioBank(3, DataType.SHORT16, 3);
-            shortBank1.appendShortData(shortData1);
-            eventBuilder2.addChild(bankBanks, shortBank1);
-
-            EvioBank shortBank2 = new EvioBank(3, DataType.SHORT16, 3);
-            shortBank2.appendShortData(shortData2);
-            eventBuilder2.addChild(bankBanks, shortBank2);
-
-            eventBuilder2.addChild(eventShort, bankBanks);
-            eventWriterNew.writeEvent(eventShort);
+        // Do we overwrite or append?
+        boolean append = false;
 
+        // Do we write to file or buffer?
+        boolean useFile = true;
 
+        // Top level event
+        EvioEvent event = null;
 
-            // each event is a trivial event containing an array of ints - all zeros
-            EventBuilder eventBuilder = new EventBuilder(5, DataType.BANK, 5);
-            EvioEvent event = eventBuilder.getEvent();
-
-            // event 1
-            EvioBank charBank1 = new EvioBank(6, DataType.CHAR8, 6);
-            charBank1.appendByteData(byteData1);
-            eventBuilder.addChild(event, charBank1);
-
-            // event 2
-            EvioBank charBank2 = new EvioBank(7, DataType.CHAR8, 7);
-            charBank2.appendByteData(byteData2);
-            eventBuilder.addChild(event, charBank2);
-
-            // event 3
-            EvioBank charBank3 = new EvioBank(8, DataType.CHAR8, 8);
-            charBank3.appendByteData(byteData3);
-            eventBuilder.addChild(event, charBank3);
-
-            // event 4
-            EvioBank charBank4 = new EvioBank(9, DataType.CHAR8, 9);
-            charBank4.appendByteData(byteData4);
-            eventBuilder.addChild(event, charBank4);
+        try {
+            // Create an event writer to write out the test events to file
+            EventWriter writer;
 
-            eventWriterNew.writeEvent(event);
+            if (useFile) {
+                writer = new EventWriter(file, xmlDictionary, append);
+            }
+            else {
+                // Create an event writer to write to buffer
+                myBuf = ByteBuffer.allocate(10000);
+                myBuf.order(ByteOrder.LITTLE_ENDIAN);
+                writer = new EventWriter(myBuf, xmlDictionary, append);
+            }
 
-            // all done writing
-            eventWriterNew.close();
+//            // Build event (bank of banks) without an EventBuilder
+//            event = new EvioEvent(1, DataType.BANK, 1);
+//
+//            // bank of segments
+//            EvioBank bankSegs = new EvioBank(2, DataType.SEGMENT, 2);
+//            event.insert(bankSegs);
+//
+//            // segment of 3 shorts
+//            EvioSegment segShorts = new EvioSegment(3, DataType.SHORT16);
+//            segShorts.setShortData(shortData);
+//            bankSegs.insert(segShorts);
+//            bankSegs.remove(segShorts);
+//
+//            // another bank of banks
+//            EvioBank bankBanks = new EvioBank(4, DataType.BANK, 4);
+//            event.insert(bankBanks);
+//
+//            // bank of chars
+//            EvioBank charBank = new EvioBank(5, DataType.CHAR8, 5);
+//            charBank.setByteData(byteData1);
+//            bankBanks.insert(charBank);
+//
+//            event.setAllHeaderLengths();
+
+            // Build event (bank of banks) with EventBuilder object
+            EventBuilder builder = new EventBuilder(1, DataType.BANK, 1);
+            event = builder.getEvent();
+
+            // bank of segments
+            EvioBank bankSegs = new EvioBank(2, DataType.SEGMENT, 2);
+            builder.addChild(event, bankSegs);
+
+            // segment of 3 shorts
+            EvioSegment segShorts = new EvioSegment(3, DataType.SHORT16);
+            segShorts.appendShortData(shortData);
+            builder.addChild(bankSegs, segShorts);
+            //builder.remove(segShorts);
+
+            // another bank of banks
+            EvioBank bankBanks = new EvioBank(4, DataType.BANK, 4);
+            builder.addChild(event, bankBanks);
+
+            // bank of chars
+            EvioBank charBank = new EvioBank(5, DataType.CHAR8, 5);
+            charBank.appendByteData(byteData1);
+            builder.addChild(bankBanks, charBank);
+            // Write event to file
+            writer.writeEvent(event);
+
+            // How much room do I have left in the buffer now?
+            if (!useFile) {
+                System.out.println("Buffer has " + myBuf.remaining() + " bytes left");
+            }
 
+            // event - bank of banks
+            EvioEvent lastEvent = new EvioEvent(33, DataType.INT32, 66);
+            // Call this BEFORE appending data!
+            lastEvent.setByteOrder(ByteOrder.LITTLE_ENDIAN);
+            lastEvent.appendIntData(intData1);
+            lastEvent.setIntData(intData2);
+            lastEvent.appendIntData(intData2);
+
+            // Write last event to file
+            writer.writeEvent(lastEvent);
+
+            // All done writing
+            writer.close();
+
+
+            // Transform segments into banks in 2 different ways
+            EvioBank segBank1 = StructureTransformer.transform(segShorts, 10);
+            StructureTransformer T = new StructureTransformer();
+            EvioBank segBank2 = T.transform(segShorts, 10);
 
+            EvioXMLDictionary dict = new EvioXMLDictionary(xmlDictionary);
+            NameProvider.setProvider(dict);
         }
         catch (IOException e) {
             e.printStackTrace();
@@ -235,157 +219,127 @@
         }
 
 
-        File fileIn = new File(fileName);
-        System.out.println("read ev file: " + fileName + " size: " + fileIn.length());
-
         try {
-            EvioReader evioReader = new EvioReader(fileName);
-            EvioEvent ev = evioReader.parseNextEvent();
-            List<BaseStructure> l = StructureFinder.getMatchingStructures(ev,"bank of shorts",
-                                                             NameProviderFactory.createNameProvider(dictionary));
-
-            if (l != null) {
-                for (BaseStructure str : l) {
-                    System.out.println("Found bank: tag = " + str.getHeader().getTag() +
-                    ", num = " + str.getHeader().getNumber());
-                }
+            EvioReader evioReader;
+            if (useFile) {
+                System.out.println("read ev file: " + fileName + ", size: " + file.length());
+                evioReader = new EvioReader(fileName);
             }
             else {
-                System.out.println("Found NO banks dude");
+                myBuf.flip();
+                evioReader = new EvioReader(myBuf);
             }
+            EventParser parser = evioReader.getParser();
 
-        }
-        catch (EvioException e) {
-            e.printStackTrace();
-        }
-        catch (IOException e) {
-            e.printStackTrace();
-        }
-    }
-
-
-    /** For testing only */
-    public static void main8(String args[]) {
-
-        //create an event writer to write out the test events.
-        String fileName  = "../../testdata/BigOut3.ev";
-        File file = new File(fileName);
-
-        ByteBuffer myBuf = ByteBuffer.allocate(10000);
-        myBuf.order(ByteOrder.LITTLE_ENDIAN);
+            IEvioListener listener = new IEvioListener() {
+                @Override
+                public void gotStructure(BaseStructure topStructure, IEvioStructure structure) {
+                    System.out.println("Parsed structure of type " + structure.getStructureType());
+                }
 
-        // xml dictionary
-        String dictionary =
-                "<xmlDict>\n" +
-                        "  <xmldumpDictEntry name=\"bank\"           tag=\"1\"   num=\"1\"/>\n" +
-                        "  <xmldumpDictEntry name=\"bank of shorts\" tag=\"2\"   num=\"2\"/>\n" +
-                        "  <xmldumpDictEntry name=\"shorts pad0\"    tag=\"3\"   num=\"3\"/>\n" +
-                        "  <xmldumpDictEntry name=\"shorts pad2\"    tag=\"4\"   num=\"4\"/>\n" +
-                        "  <xmldumpDictEntry name=\"bank of chars\"  tag=\"5\"   num=\"5\"/>\n" +
-                        "  <xmldumpDictEntry name=\"chars pad0\"     tag=\"6\"   num=\"6\"/>\n" +
-                        "  <xmldumpDictEntry name=\"chars pad3\"     tag=\"7\"   num=\"7\"/>\n" +
-                        "  <xmldumpDictEntry name=\"chars pad2\"     tag=\"8\"   num=\"8\"/>\n" +
-                        "  <xmldumpDictEntry name=\"chars pad1\"     tag=\"9\"   num=\"9\"/>\n" +
-                "</xmlDict>";
+                @Override
+                public void startEventParse(BaseStructure structure) {
+                    System.out.println("Starting event parse");
+                }
 
-        // data
-        byte[]  byteData1   = new byte[]  {1,2,3,4};
-        byte[]  byteData2   = new byte[]  {1,2,3,4,5};
-        byte[]  byteData3   = new byte[]  {1,2,3,4,5,6};
-        byte[]  byteData4   = new byte[]  {1,2,3,4,5,6,7};
-        short[] shortData1  = new short[] {11,22};
-        short[] shortData2  = new short[] {11,22,33};
+                @Override
+                public void endEventParse(BaseStructure structure) {
+                    System.out.println("Ended event parse");
+                }
+            };
 
-        try {
-            EventWriter eventWriterNew = new EventWriter(file, 100, 3,
-                                                         ByteOrder.BIG_ENDIAN, dictionary, null);
+            parser.addEvioListener(listener);
 
-            // event - bank of banks
-            EventBuilder eventBuilder2 = new EventBuilder(1, DataType.BANK, 1);
-            EvioEvent eventShort = eventBuilder2.getEvent();
+            // Get any existing dictionary (should be the same as "xmlDictionary")
+            String xmlDictString = evioReader.getDictionaryXML();
+            EvioXMLDictionary dictionary = null;
 
-            // bank of short banks
-            EvioBank bankBanks = new EvioBank(2, DataType.BANK, 2);
+            if (xmlDictString == null) {
+                System.out.println("Ain't got no dictionary!");
+            }
+            else {
+                // Create dictionary object from xml string
+                dictionary = new EvioXMLDictionary(xmlDictString);
+                System.out.println("Got a dictionary:\n" + dictionary.toString());
+            }
 
-            // 3 shorts
-            EvioBank shortBank1 = new EvioBank(3, DataType.SHORT16, 3);
-            shortBank1.appendShortData(shortData1);
-            eventBuilder2.addChild(bankBanks, shortBank1);
+            // How many events in the file?
+            int evCount = evioReader.getEventCount();
+            System.out.println("Read file, got " + evCount + " events:\n");
+
+            // Use the "random access" capability to look at last event (starts at 1)
+            EvioEvent ev = evioReader.parseEvent(evCount);
+            System.out.println("Last event = " + ev.toString());
+
+            // Print out any data in the last event.
+            //
+            // In the writing example, the data for this event was set to
+            // be little endian so we need to read it in that way too
+            ev.setByteOrder(ByteOrder.LITTLE_ENDIAN);
+            int[] intData = ev.getIntData();
+            if (intData != null) {
+                for (int i=0; i < intData.length; i++) {
+                    System.out.println("intData[" + i + "] = " + intData[i]);
+                }
+            }
 
-            EvioBank shortBank2 = new EvioBank(4, DataType.SHORT16, 4);
-            shortBank2.appendShortData(shortData2);
-            eventBuilder2.addChild(bankBanks, shortBank2);
+            // Use the dictionary
+            if (dictionary != null) {
+                String eventName = dictionary.getName(ev);
+                System.out.println("Name of last event = " + eventName);
+            }
 
-            eventBuilder2.addChild(eventShort, bankBanks);
-            eventWriterNew.writeEvent(eventShort);
+            // Use sequential access to events
+            while ( (ev = evioReader.parseNextEvent()) != null) {
+                System.out.println("Event = " + ev.toString());
+            }
 
+            // Go back to the beginning of file/buffer
+            evioReader.rewind();
 
+            // Search for banks/segs/tagsegs with a particular tag & num pair of values
+            int tag=1, num=1;
+            List<BaseStructure> list = StructureFinder.getMatchingBanks(
+                                            (ev = evioReader.parseNextEvent()), tag, num);
+            System.out.println("Event = " + ev.toString());
+            for (BaseStructure s : list) {
+                System.out.println("Evio structure named \"" + dictionary.getName(s) +
+                                    "\" has tag=1 & num=1");
+            }
 
-            // each event is a trivial event containing an array of ints - all zeros
-            EventBuilder eventBuilder = new EventBuilder(5, DataType.BANK, 5);
-            EvioEvent event = eventBuilder.getEvent();
-
-            // event 1
-            EvioBank charBank1 = new EvioBank(6, DataType.CHAR8, 6);
-            charBank1.appendByteData(byteData1);
-            eventBuilder.addChild(event, charBank1);
-
-            // event 2
-            EvioBank charBank2 = new EvioBank(7, DataType.CHAR8, 7);
-            charBank2.appendByteData(byteData2);
-            eventBuilder.addChild(event, charBank2);
-
-            // event 3
-            EvioBank charBank3 = new EvioBank(8, DataType.CHAR8, 8);
-            charBank3.appendByteData(byteData3);
-            eventBuilder.addChild(event, charBank3);
-
-            // event 4
-            EvioBank charBank4 = new EvioBank(9, DataType.CHAR8, 9);
-            charBank4.appendByteData(byteData4);
-            eventBuilder.addChild(event, charBank4);
+            // ------------------------------------------------------------------
+            // Search for banks/segs/tagsegs with a custom set of search criteria
+            // ------------------------------------------------------------------
+
+            // This filter selects Segment structures that have odd numbered tags.
+            class myEvioFilter implements IEvioFilter {
+                public boolean accept(StructureType structureType, IEvioStructure struct) {
+                    return (structureType == StructureType.SEGMENT &&
+                            (struct.getHeader().getTag() % 2 == 1));
+                }
+            };
 
-            eventWriterNew.writeEvent(event);
+            myEvioFilter filter = new myEvioFilter();
+            list = StructureFinder.getMatchingStructures(event, filter);
+            if (list != null) {
+                System.out.println("list size = " + list.size());
+                for (BaseStructure s : list) {
+                    System.out.println("Evio structure named " + dictionary.getName(s) +
+                                       " is a segment with an odd numbered tag");
+                }
+            }
 
-            // all done writing
-            eventWriterNew.close();
-        }
-        catch (IOException e) {
-            e.printStackTrace();
+            // ------------------------------------------------------------------
         }
         catch (EvioException e) {
             e.printStackTrace();
         }
-
-
-        File fileIn = new File(fileName);
-        System.out.println("read ev file: " + fileName + " size: " + fileIn.length());
-
-        try {
-            EvioReader evioReader = new EvioReader(fileName);
-   System.out.println("EvioFile exe: now do output to xml file");
-            evioReader.toXMLFile("/daqfs/home/timmer/coda/jevio-4.0/testdata/BigOut3.xml");
-
-//             ByteBuffer mBuf = evioReader.getMappedByteBuffer();
-
-//             System.out.println("i    File   myBuffer");
-//             for (int i=0; i < mBuf.limit(); i+=4) {
-//                 System.out.println((i/4)+ "   " + mBuf.getInt(i) + "   " + myBuf.getInt(i));
-//             }
-//             for (int i=0; i < mBuf.limit(); i+=4) {
-//                 if (mBuf.getInt(i) != myBuf.getInt(i)) {
-//                     System.out.print((i/4)+" ");
-//                 }
-//             }
-
-        }
         catch (IOException e) {
             e.printStackTrace();
         }
     }
 
 
-
     /** For testing only */
     public static void main9(String args[]) {
 
@@ -394,7 +348,6 @@
         File file = new File(fileName);
 
         ByteBuffer myBuf = ByteBuffer.allocate(800);
-        //myBuf.order(ByteOrder.LITTLE_ENDIAN);
 
         // xml dictionary
         String dictionary =
@@ -404,17 +357,9 @@
                         "  <xmldumpDictEntry name=\"shorts pad0\"    tag=\"3\"   num=\"1.2.3\"/>\n" +
                         "  <xmldumpDictEntry name=\"shorts pad2\"    tag=\"4\"   num=\"1.2.4\"/>\n" +
                         "  <xmldumpDictEntry name=\"bank of chars\"  tag=\"5\"   num=\"5\"/>\n" +
-                        "  <xmldumpDictEntry name=\"chars pad0\"     tag=\"6\"   num=\"5.6\"/>\n" +
-                        "  <xmldumpDictEntry name=\"chars pad3\"     tag=\"7\"   num=\"5.7\"/>\n" +
-                        "  <xmldumpDictEntry name=\"chars pad2\"     tag=\"8\"   num=\"5.8\"/>\n" +
-                        "  <xmldumpDictEntry name=\"chars pad1\"     tag=\"9\"   num=\"5.9\"/>\n" +
                 "</xmlDict>";
 
         // data
-        byte[]  byteData1   = new byte[]  {1,2,3,4};
-        byte[]  byteData2   = new byte[]  {1,2,3,4,5};
-        byte[]  byteData3   = new byte[]  {1,2,3,4,5,6};
-        byte[]  byteData4   = new byte[]  {1,2,3,4,5,6,7};
         short[] shortData1  = new short[] {11,22};
         short[] shortData2  = new short[] {11,22,33};
 
@@ -564,4 +509,267 @@
          }
      }
 
+
+    /**
+       3 block headers (first 2 have 2 extra words each, last has 1 extra word).
+       First block has 2 events. Second has 3 events.
+       Last is empty final block.
+    */
+    static int data1[] = {
+        0x00000014,
+        0x00000001,
+        0x0000000A,
+        0x00000002,
+        0x00000000,
+        0x00000004,
+        0x00000000,
+        0xc0da0100,
+        0x00000003,
+        0x00000002,
+
+        0x00000004,
+        0x00010101,
+        0x00000001,
+        0x00000001,
+        0x00000001,
+
+        0x00000004,
+        0x00010101,
+        0x00000002,
+        0x00000002,
+        0x00000002,
+
+        0x00000019,
+        0x00000002,
+        0x0000000A,
+        0x00000003,
+        0x00000000,
+        0x00000004,
+        0x00000000,
+        0xc0da0100,
+        0x00000001,
+        0x00000002,
+
+        0x00000004,
+        0x00010101,
+        0x00000003,
+        0x00000003,
+        0x00000003,
+
+        0x00000004,
+        0x00010101,
+        0x00000004,
+        0x00000004,
+        0x00000004,
+
+        0x00000004,
+        0x00010101,
+        0x00000005,
+        0x00000005,
+        0x00000005,
+
+        0x00000009,
+        0x00000003,
+        0x00000009,
+        0x00000000,
+        0x00000000,
+        0x00000204,
+        0x00000000,
+        0xc0da0100,
+        0x00000003,
+    };
+
+
+    /** For writing out a 5GByte file. */
+    public static void main(String args[]) {
+
+        // write evio file that has extra words in headers
+        if (false) {
+            try {
+                byte[] be  = ByteDataTransformer.toBytes(data1, ByteOrder.BIG_ENDIAN);
+                ByteBuffer buf = ByteBuffer.wrap(be);
+                String fileName  = "/local/scratch/HeaderFile.ev";
+                File file = new File(fileName);
+                FileOutputStream fileOutputStream = new FileOutputStream(file);
+                FileChannel fileChannel = fileOutputStream.getChannel();
+                fileChannel.write(buf);
+                fileChannel.close();
+            }
+            catch (Exception e) {
+                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
+            }
+
+        }
+
+        //create an event writer to write out the test events.
+        //String fileName  = "/local/scratch/BigFile.ev";
+        String fileName  = "/local/scratch/LittleFile.ev";
+        //String fileName  = "/local/scratch/HeaderFile.ev";
+        File file = new File(fileName);
+        EvioEvent event;
+
+        if (false) {
+// data
+            int[] intData  = new int[2500000];
+            Arrays.fill(intData, 23);
+
+
+            try {
+                EventWriter eventWriterNew = new EventWriter(file, 1000, 3, ByteOrder.BIG_ENDIAN, null, null);
+
+                // event - bank of banks
+                EventBuilder eventBuilder2 = new EventBuilder(1, DataType.INT32, 1);
+                event = eventBuilder2.getEvent();
+                event.appendIntData(intData);
+
+                // 300 ev/file * 10MB/ev = 3GB/file
+                for (int i=0; i < 300; i++) {
+                    eventWriterNew.writeEvent(event);
+                    System.out.print(".");
+                }
+                System.out.println("\nDONE");
+
+                // all done writing
+                eventWriterNew.close();
+            }
+            catch (IOException e) {
+                e.printStackTrace();
+            }
+            catch (EvioException e) {
+                e.printStackTrace();
+            }
+        }
+
+        if (false) {
+// data
+            int[] intData  = new int[1];
+
+
+            try {
+                EventWriter eventWriterNew = new EventWriter(file, 1000, 3, ByteOrder.BIG_ENDIAN, null, null);
+
+                // event - bank of banks
+                EventBuilder eventBuilder2 = new EventBuilder(1, DataType.INT32, 1);
+                event = eventBuilder2.getEvent();
+                event.appendIntData(intData);
+
+                // 300 ev/file * 4bytes/ev = 1.2kB/file
+                for (int i=0; i < 300; i++) {
+                    eventWriterNew.writeEvent(event);
+                    System.out.print(".");
+                }
+                System.out.println("\nDONE");
+
+                // all done writing
+                eventWriterNew.close();
+            }
+            catch (IOException e) {
+                e.printStackTrace();
+            }
+            catch (EvioException e) {
+                e.printStackTrace();
+            }
+        }
+
+        if (true) {
+            System.out.println("\nTRY READING");
+
+            File fileIn = new File(fileName);
+            System.out.println("read ev file: " + fileName + " size: " + fileIn.length());
+            try {
+                EvioReader fileReader = new EvioReader(fileName);
+                int evCount = fileReader.getEventCount();
+                System.out.println("\nnum ev = " + evCount);
+                System.out.println("dictionary = " + fileReader.getDictionaryXML() + "\n");
+
+                long t2, t1 = System.currentTimeMillis();
+     // test sequential after random
+                event = fileReader.getEvent(290);
+                System.out.println("Got 290th event");
+
+                int counter = 0;
+                while ( (event = fileReader.parseNextEvent()) != null) {
+                    if (event == null) {
+                        System.out.println("We got a NULL event !!!");
+                        return;
+                    }
+                        System.out.println("Event #" + counter++ + " =\n" + event);
+
+//                    if (counter++ %10 ==0) {
+//                        System.out.println("Event #" + counter + " =\n" + event);
+//                        int[] d = event.getIntData();
+//                        System.out.println("Data[0] = " + d[0] + ", Data[last] = " + d[d.length-1]);
+//                        //            System.out.println("Event = \n" + event.toXML());
+//                        //            while ( (event = fileReader.parseNextEvent()) != null) {
+//                        //               System.out.println("Event = " + event.toString());
+//                        //            }
+//                    }
+                }
+
+                t2 = System.currentTimeMillis();
+                System.out.println("Sequential Time = " + (t2-t1) + " milliseconds");
+
+                fileReader.rewind();
+
+                t1 = System.currentTimeMillis();
+
+                for (int i=1; i <= evCount; i++) {
+                    event = fileReader.getEvent(i);
+                    if (event == null) {
+                        System.out.println("We got a NULL event !!!");
+                        return;
+                    }
+
+//                    if (i %10 == 0) {
+//                        System.out.println("Event #" + i + " =\n" + event);
+//                        int[] d = event.getIntData();
+//                        System.out.println("Data[0] = " + d[0] + ", Data[last] = " + d[d.length-1]);
+//                    }
+
+                }
+
+                t2 = System.currentTimeMillis();
+                System.out.println("Random access Time = " + (t2-t1) + " milliseconds");
+            }
+            catch (IOException e) {
+                e.printStackTrace();
+            }
+            catch (EvioException e) {
+                e.printStackTrace();
+            }
+        }
+
+
+        if (false) {
+            System.out.println("\nTRY READING");
+
+            File fileIn = new File(fileName);
+            System.out.println("read ev file: " + fileName + " size: " + fileIn.length());
+            try {
+                EvioReader fileReader = new EvioReader(fileName);
+//                System.out.println("\nev count= " + fileReader.getEventCount());
+//                System.out.println("dictionary = " + fileReader.getDictionaryXML() + "\n");
+//
+//
+//                System.out.println("ev count = " + fileReader.getEventCount());
+                EvioEvent ev = fileReader.getEvent(1);
+                System.out.println("Event = \n" + ev);
+
+
+            }
+            catch (IOException e) {
+                e.printStackTrace();
+            }
+            catch (EvioException e) {
+                e.printStackTrace();
+            }
+        }
+
+
+        System.out.println("DONE READING");
+
+    }
+
+
+
 }

jevio-base/src/main/java/org/jlab/coda/jevio/test
CompositeTester.java 1.2 -> 1.3
diff -u -r1.2 -r1.3
--- CompositeTester.java	8 Mar 2012 20:39:27 -0000	1.2
+++ CompositeTester.java	3 Apr 2013 20:07:47 -0000	1.3
@@ -246,14 +246,101 @@
 
         try {
             EvioEvent ev = new EvioEvent(0, DataType.COMPOSITE, 0);
+            ev.appendCompositeData(new CompositeData[] {cData});
+
+            // Write it to this file
+            String fileName  = "./composite.dat";
+
+            EventWriter writer = new EventWriter(fileName);
+            writer.writeEvent(ev);
+            writer.close();
+        }
+        catch (Exception e) {
+            e.printStackTrace();
+        }
+
+
+    }
+
+
+    /**
+     * More complicated example of providing a format string and some data
+     * in order to create a CompositeData object.
+     */
+    public static void main(String args[]) {
+
+        // Create a CompositeData object ...
+
+        // Format to write a N shorts, 1 float, 1 double a total of N times
+        String format = "N(I,F)";
+
+System.out.println("format = " + format);
+
+        // Now create some data
+        CompositeData.Data myData1 = new CompositeData.Data();
+        myData1.addN(1);
+        myData1.addInt(1); // use array for convenience
+        myData1.addFloat(1.0F);
+
+        // Now create some data
+        CompositeData.Data myData2 = new CompositeData.Data();
+        myData2.addN(1);
+        myData2.addInt(2); // use array for convenience
+        myData2.addFloat(2.0F);
+
+        // Now create some data
+        CompositeData.Data myData3 = new CompositeData.Data();
+        myData3.addN(1);
+        myData3.addInt(3); // use array for convenience
+        myData3.addFloat(3.0F);
+
+
+System.out.println("Create composite data objects");
+
+        // Create CompositeData object
+        CompositeData[] cData = new CompositeData[3];
+        try {
+            cData[0] = new CompositeData(format, 1, myData1, 1 ,1);
+            cData[1] = new CompositeData(format, 2, myData2, 2 ,2);
+            cData[2] = new CompositeData(format, 3, myData3, 3 ,3);
+        }
+        catch (EvioException e) {
+            e.printStackTrace();
+            System.exit(-1);
+        }
+
+        // Print it out
+        System.out.println("Print composite data objects");
+        printCompositeDataObject(cData[0]);
+        printCompositeDataObject(cData[1]);
+        printCompositeDataObject(cData[2]);
+
+        try {
+            EvioEvent ev = new EvioEvent(0, DataType.COMPOSITE, 0);
             ev.appendCompositeData(cData);
 
             // Write it to this file
             String fileName  = "./composite.dat";
 
+System.out.println("WRITE FILE:");
             EventWriter writer = new EventWriter(fileName);
             writer.writeEvent(ev);
             writer.close();
+
+            // Read it from file
+System.out.println("READ FILE & PRINT CONTENTS:");
+            EvioReader reader = new EvioReader(fileName);
+            EvioEvent evR = reader.parseNextEvent();
+            BaseStructureHeader h = evR.getHeader();
+            System.out.println("event: tag = " + h.getTag() +
+                                ", type = " + h.getDataTypeName() + ", len = " + h.getLength());
+            if (evR != null) {
+                CompositeData[] cDataR = evR.getCompositeData();
+                for (CompositeData cd : cDataR) {
+                    printCompositeDataObject(cd);
+                }
+            }
+
         }
         catch (Exception e) {
             e.printStackTrace();
@@ -264,8 +351,9 @@
 
 
 
+
     /** For testing only */
-    public static void main(String args[]) {
+    public static void main4(String args[]) {
 
         //create an event writer to write out the test events.
         String fileName  = "/daqfs/home/timmer/coda/evio-4.0.sergey/Linux-x86_64/bin/sample.dat";
@@ -306,6 +394,7 @@
             type =  types.get(i);
             System.out.print(String.format("type = %9s, val = ", type));
             switch (type) {
+                case NVALUE:
                 case INT32:
                 case UINT32:
                 case UNKNOWN32:

jevio-base/src/main/java/org/jlab/coda/jevio/graphics
EventTreePanel.java 1.2 -> 1.3
diff -u -r1.2 -r1.3
--- EventTreePanel.java	8 Mar 2012 20:39:27 -0000	1.2
+++ EventTreePanel.java	3 Apr 2013 20:07:47 -0000	1.3
@@ -372,9 +372,14 @@
 
             case COMPOSITE:
                 try {
-                    CompositeData cData = structure.getCompositeData();
+                    CompositeData[] cData = structure.getCompositeData();
                     if (cData != null) {
-                        textArea.append(cData.toString(intsInHex));
+                        for (int i=0; i < cData.length; i++) {
+                            textArea.append("composite data object ");
+                            textArea.append(i + ":\n");
+                            textArea.append(cData[i].toString(intsInHex));
+                            textArea.append("\n\n");
+                        }
                     }
                     else {
                         textArea.append("null data\n");

jevio-base/src/main/java/org/jlab/coda/jevio/graphics
EventTreeMenu.java 1.2 -> 1.3
diff -u -r1.2 -r1.3
--- EventTreeMenu.java	20 Mar 2012 23:21:49 -0000	1.2
+++ EventTreeMenu.java	3 Apr 2013 20:07:47 -0000	1.3
@@ -5,7 +5,10 @@
 import java.io.File;
 import java.io.IOException;
 import java.util.Arrays;
+import java.util.EventListener;
+
 import javax.swing.*;
+import javax.swing.event.EventListenerList;
 import javax.swing.filechooser.FileNameExtensionFilter;
 import javax.swing.border.LineBorder;
 import javax.swing.border.EmptyBorder;
@@ -108,7 +111,13 @@
     /** Thread to update cMsg queue size in GUI. */
     private UpdateThread cmsgUpdateThread;
 
-
+    //----------------------------
+    // General function
+    //----------------------------
+	/**
+	 * Listener list for structures (banks, segments, tagsegments) encountered while processing an event.
+	 */
+	private EventListenerList evioListenerList;
 
     /**
      * This class is a thread which updates the number of events existing in the queue
@@ -528,7 +537,7 @@
              }
         };
         etItem.addActionListener(etListener);
-        etItem.setEnabled(false); // turn off ET source for now
+        // etItem.setEnabled(false); // turn off ET source for now
 
         ButtonGroup group = new ButtonGroup();
         group.add(fileItem);
@@ -1130,6 +1139,8 @@
                 e.printStackTrace();
             }
         }
+        connectEvioListeners();     // Connect Listeners to the parser.
+        
         return evioFileReader;
     }
 
@@ -1174,6 +1185,7 @@
             evioFileReader = null;
             e.printStackTrace();
         }
+        connectEvioListeners();     // Connect Listeners to the parser.
         return evioFileReader;
     }
 
@@ -1242,5 +1254,39 @@
         }
     }
 
+	/**
+	 * Add an Evio listener. Evio listeners listen for structures encountered when an event is being parsed.
+	 * The listeners are passed to the EventParser once a file is opened.
+	 * @param listener The Evio listener to add.
+	 */
+	public void addEvioListener(IEvioListener listener) {
+
+		if (listener == null) {
+			return;
+		}
+
+		if (evioListenerList == null) {
+			evioListenerList = new EventListenerList();
+		}
+
+		evioListenerList.add(IEvioListener.class, listener);
+	}
+	/**
+	 * Connect the listeners in the evioListenerList to the EventParser
+	 */
+	private void connectEvioListeners(){
+		
+		if (evioListenerList == null) {
+			return;
+		}
+
+		EventParser parser = getEvioFileReader().getParser();
+		
+		EventListener listeners[] = evioListenerList.getListeners(IEvioListener.class);
+
+		for (int i = 0; i < listeners.length; i++) {
+			parser.addEvioListener((IEvioListener)listeners[i]);
+		}		
+	}
 
-}
\ No newline at end of file
+}
CVSspam 0.2.12


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