Commit in jevio-base/src/main/java/org/jlab/coda/jevio on MAIN
BankHeader.java+1021.2 -> 1.3
BaseStructure.java+20741.2 -> 1.3
BaseStructureHeader.java+2471.2 -> 1.3
BlockHeaderV2.java+501added 1.1
BlockHeaderV4.java+527added 1.1
ByteDataTransformer.java+14401.2 -> 1.3
CompositeData.java+1795added 1.1
DataType.java+1171.2 -> 1.3
Demo.java+1271.2 -> 1.3
Environment.java+2031.2 -> 1.3
EventBuilder.java+4521.2 -> 1.3
EventParser.java+6141.2 -> 1.3
EventWriter.java+10151.2 -> 1.3
EvioBank.java+1361.2 -> 1.3
EvioDictionaryEntry.java+2441.2 -> 1.3
EvioEvent.java+2771.2 -> 1.3
EvioException.java+431.2 -> 1.3
EvioFile.java+9371.2 -> 1.3
EvioFileTest.java+1601.2 -> 1.3
EvioReader.java+1094added 1.1
EvioSegment.java+961.2 -> 1.3
EvioTagSegment.java+971.2 -> 1.3
EvioXMLDictionary.java+2401.2 -> 1.3
IBlockHeader.java+137added 1.1
IEvioFilter.java+311.2 -> 1.3
IEvioListener.java+351.2 -> 1.3
IEvioProgressListener.java+111.2 -> 1.3
IEvioStructure.java+1171.2 -> 1.3
IEvioWriter.java+181.2 -> 1.3
INameProvider.java+261.2 -> 1.3
NameProvider.java+581.2 -> 1.3
NameProviderFactory.java+541.2 -> 1.3
SegmentHeader.java+931.2 -> 1.3
StructureFinder.java+1371.2 -> 1.3
StructureTransformer.java+208added 1.1
StructureType.java+631.2 -> 1.3
TagSegmentHeader.java+911.2 -> 1.3
graphics/EventInfoPanel.java+184added 1.1
        /EventSource.java+12added 1.1
        /EventTreePanel.java+436added 1.1
        /HeaderPanel.java+127added 1.1
        /NamedLabel.java+83added 1.1
test/CompositeTester.java+324added 1.1
    /FileTest.java+567added 1.1
    /Tester.java+743added 1.1
+16093
14 added + 31 modified, total 45 files
adding sources for evio 4

jevio-base/src/main/java/org/jlab/coda/jevio
BankHeader.java 1.2 -> 1.3
diff -N BankHeader.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ BankHeader.java	28 Feb 2012 19:41:35 -0000	1.3
@@ -0,0 +1,102 @@
+package org.jlab.coda.jevio;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * This the header for an evio bank structure (<code>EvioBank</code>). It does not contain the raw data, just the
+ * header. Note: since an "event" is really just the outermost bank, this is also the header for an
+ * <code>EvioEvent</code>.
+ * 
+ * @author heddle
+ * 
+ */
+public final class BankHeader extends BaseStructureHeader {
+	
+	/**
+	 * Null constructor.
+	 */
+	public BankHeader() {
+	}
+
+	/**
+	 * Constructor
+	 * @param tag the tag for the bank header.
+	 * @param dataType the data type for the content of the bank.
+	 * @param num sometimes, but not necessarily, an ordinal enumeration.
+	 */
+	public BankHeader(int tag, DataType dataType, int num) {
+		super(tag, dataType, num);
+	}
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public int getHeaderLength() {return 2;}
+
+    /**
+     * {@inheritDoc}
+     */
+    protected void toArray(byte[] bArray, int offset, ByteOrder order) {
+        try {
+            // length first
+            ByteDataTransformer.toBytes(length, order, bArray, offset);
+
+            if (order == ByteOrder.BIG_ENDIAN) {
+                ByteDataTransformer.toBytes((short)tag, order, bArray, offset+4);
+                // lowest 6 bits are dataType, upper 2 bits are padding
+                bArray[offset+6] = (byte)((dataType.getValue() & 0x3f) | (padding << 6));
+                bArray[offset+7] = (byte)number;
+            }
+            else {
+                bArray[offset+4] = (byte)number;
+                bArray[offset+5] = (byte)((dataType.getValue() & 0x3f) | (padding << 6));
+                ByteDataTransformer.toBytes((short)tag, order, bArray, offset+6);
+            }
+        }
+        catch (EvioException e) {e.printStackTrace();}
+    }
+
+
+	/**
+	 * Obtain a string representation of the bank header.
+	 * 
+	 * @return a string representation of the bank header.
+	 */
+	@Override
+	public String toString() {
+		StringBuffer sb = new StringBuffer(512);
+		sb.append(String.format("bank length: %d\n", length));
+		sb.append(String.format("number:      %d\n", number));
+		sb.append(String.format("data type:   %s\n", getDataTypeName()));
+        sb.append(String.format("tag:         %d\n", tag));
+        sb.append(String.format("padding:     %d\n", padding));
+		return sb.toString();
+	}
+	
+	/**
+	 * Write myself out a byte buffer. This write is relative--i.e., it uses the current position of the buffer.
+	 * 
+	 * @param byteBuffer the byteBuffer to write to.
+	 * @return the number of bytes written, which for a BankHeader is 8.
+	 */
+	@Override
+	public int write(ByteBuffer byteBuffer) {
+		byteBuffer.putInt(length);
+        
+        if (byteBuffer.order() == ByteOrder.BIG_ENDIAN) {
+            byteBuffer.putShort((short)tag);
+            byteBuffer.put((byte)((dataType.getValue() & 0x3f) | (padding << 6)));
+            byteBuffer.put((byte)number);
+        }
+        else {
+            byteBuffer.put((byte)number);
+            byteBuffer.put((byte)((dataType.getValue() & 0x3f) | (padding << 6)));
+            byteBuffer.putShort((short)tag);
+        }
+
+		return 8;
+	}
+
+}

jevio-base/src/main/java/org/jlab/coda/jevio
BaseStructure.java 1.2 -> 1.3
diff -N BaseStructure.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ BaseStructure.java	28 Feb 2012 19:41:35 -0000	1.3
@@ -0,0 +1,2074 @@
+package org.jlab.coda.jevio;
+
+import java.nio.*;
+import java.util.*;
+import java.io.UnsupportedEncodingException;
+import java.io.StringWriter;
+
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.MutableTreeNode;
+import javax.swing.tree.TreeNode;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import javax.xml.stream.XMLOutputFactory;
+
+/**
+ * This is the base class for all evio structures: Banks, Segments, and TagSegments.
+ * It implements <code>MutableTreeNode</code> because a tree representation of
+ * events is created when a new event is parsed.<p>
+ * Note that using an EventBuilder for the same event in more than one thread
+ * can cause problems. For example the boolean lengthsUpToDate in this class
+ * would need to be volatile.
+ * 
+ * @author heddle
+ * @author timmer - add byte order tracking, make setAllHeaderLengths more efficient
+ *
+ */
+public abstract class BaseStructure implements IEvioStructure, MutableTreeNode, IEvioWriter {
+
+	/**
+	 * Holds the header of the bank.
+	 */
+	protected BaseStructureHeader header;
+
+    /**
+     * The raw data of the structure.
+     */
+    protected byte rawBytes[];
+
+    /**
+     * Used if raw data should be interpreted as chars.
+     * The reason rawBytes is not used directly is because
+     * it may be padded and it may not, depending on whether
+     * this object was created by EvioReader or by EventBuilder, etc., etc.
+     * We don't want to return rawBytes when a user calls getByteData() if there
+     * are padding bytes in it.
+     */
+    protected byte charData[];
+
+	/**
+	 * Used if raw data should be interpreted as ints.
+	 */
+	protected int intData[];
+
+	/**
+	 * Used if raw data should be interpreted as longs.
+	 */
+	protected long longData[];
+
+	/**
+	 * Used if raw data should be interpreted as shorts.
+	 */
+	protected short shortData[];
+
+	/**
+	 * Used if raw data should be interpreted as doubles.
+	 */
+	protected double doubleData[];
+
+    /**
+     * Used if raw data should be interpreted as doubles.
+     */
+    protected float floatData[];
+
+    /**
+     * Used if raw data should be interpreted as composite type.
+     */
+    protected CompositeData compositeData;
+
+    /**
+     * Used if raw data should be interpreted as a string.
+     */
+    protected StringBuffer stringData;
+
+    /**
+     * Used if raw data should be interpreted as a string.
+     */
+    protected LinkedList<String> stringsList;
+
+    /**
+     * Keep track of end of the last string added to stringData (including null but not padding).
+     */
+    protected int stringEnd;
+
+    /**
+     * The number of stored data items like number of banks, ints, floats, etc.
+     * (not the size in ints or bytes). Some items may be padded such as shorts
+     * and bytes. This will tell the meaningful number of such data items.
+     * In the case of containers, returns number of bytes not in header.
+     */
+    protected int numberDataItems;
+
+    /** Bytes with which to pad short and byte data. */
+    private static byte[] padValues = {0,0,0};
+
+    /** Number of bytes to pad short and byte data. */
+    private static int[]  padCount  = {0,3,2,1};
+
+    /**
+     * Give the XML output proper indentation.
+     */
+    protected String xmlIndent = "";
+
+    /**
+     * Name of this object as an XML element.
+     */
+    protected String xmlElementName;
+
+    /**
+     * Name of this object's contents as an XML attribute if it is a structure type.
+     */
+    protected String xmlContentAttributeName;
+
+    /**
+     * Endianness of the data if appropriate,
+     * either {@link ByteOrder#BIG_ENDIAN} or {@link ByteOrder#LITTLE_ENDIAN}.
+     */
+    protected ByteOrder byteOrder;
+
+	/**
+	 * The parent of the structure. If it is an "event", the parent is null. This is used for creating trees for a
+	 * variety of purposes (not necessarily graphical.)
+	 */
+	private BaseStructure parent;
+
+	/**
+	 * Holds the children of this structure. This is used for creating trees for a variety of purposes
+     * (not necessarily graphical.)
+	 */
+	protected Vector<BaseStructure> children;
+
+    /**
+     * Keep track of whether header length data is up-to-date or not.
+     */
+    protected boolean lengthsUpToDate;
+
+    /**
+     * Is this structure a leaf? Leaves are structures with no children.
+     */
+    protected boolean isLeaf = true;
+
+
+    /**
+	 * Constructor using a provided header
+	 * 
+	 * @param header the header to use.
+	 * @see BaseStructureHeader
+	 */
+	public BaseStructure(BaseStructureHeader header) {
+		this.header = header;
+        // by default we're big endian since this is java
+        byteOrder = ByteOrder.BIG_ENDIAN;
+        setXmlNames();
+	}
+
+    /**
+     * 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.
+     *
+     * @param structure BaseStructure from which to copy data.
+     */
+    public void copy(BaseStructure structure) {
+
+        if (structure == null) return;
+
+        // reinitialize this base structure first
+        rawBytes      = null;
+        charData      = null;
+        intData       = null;
+        longData      = null;
+        shortData     = null;
+        doubleData    = null;
+        floatData     = null;
+        compositeData = null;
+        stringData    = null;
+        stringsList   = null;
+        stringEnd     = 0;
+
+        if (children != null) {
+            children.clear();
+        }
+        else {
+            children = new Vector<BaseStructure>(10);
+        }
+
+        // copy over some stuff from other structure
+        isLeaf = structure.isLeaf;
+        lengthsUpToDate = structure.lengthsUpToDate;
+        byteOrder = structure.byteOrder;
+        numberDataItems = structure.numberDataItems;
+
+        xmlIndent = structure.xmlIndent;
+        xmlElementName = structure.xmlElementName;
+        xmlContentAttributeName =  structure.xmlContentAttributeName;
+
+        if (structure.getRawBytes() != null) {
+            setRawBytes(structure.getRawBytes().clone());
+        }
+
+        DataType type = structure.getHeader().getDataType();
+
+        switch (type)  {
+            case SHORT16:
+            case USHORT16:
+                if (structure.shortData != null) {
+                    shortData = structure.shortData.clone();
+                }
+                break;
+
+            case INT32:
+            case UINT32:
+                if (structure.intData != null) {
+                    intData = structure.intData.clone();
+                }
+                break;
+
+            case LONG64:
+            case ULONG64:
+                if (structure.longData != null) {
+                    longData = structure.longData.clone();
+                }
+                break;
+
+            case FLOAT32:
+                if (structure.floatData != null) {
+                    floatData = structure.floatData.clone();
+                }
+                break;
+
+            case DOUBLE64:
+                if (structure.doubleData != null) {
+                    doubleData = structure.doubleData.clone();
+                }
+                break;
+
+            case CHAR8:
+            case UCHAR8:
+                if (structure.charData != null) {
+                    charData = structure.charData.clone();
+                }
+                break;
+
+            case CHARSTAR8:
+                if (structure.stringsList != null) {
+                    stringsList = new LinkedList<String>();
+                    stringsList.addAll(structure.stringsList);
+                    stringData = new StringBuffer(structure.stringData);
+                    stringEnd = structure.stringEnd;
+                }
+                break;
+
+            case COMPOSITE:
+                if (structure.compositeData != null) {
+                    compositeData = (CompositeData) structure.compositeData.clone();
+                }
+                break;
+
+            case ALSOBANK:
+            case ALSOSEGMENT:
+            case BANK:
+            case SEGMENT:
+            case TAGSEGMENT:
+                for (BaseStructure kid : structure.children) {
+                    children.add(kid);
+                }
+                break;
+
+            default:
+        }
+    }
+
+
+	/**
+	 * A convenience method use instead of "instanceof" to see what type of structure we have. Note: this returns the
+	 * type of this structure, not the type of data (the content type) this structure holds.
+	 * 
+	 * @return the <code>StructureType</code> of this structure.
+	 * @see StructureType
+	 */
+	@Override
+	public abstract StructureType getStructureType();
+
+    /**
+     * This is a method that must be filled in by subclasses. Write this structure out as an XML record.
+     *
+     * @param xmlWriter the writer used to write the events. It is tied to an open file.
+     */
+    public abstract void toXML(XMLStreamWriter xmlWriter);
+
+	/**
+	 * Get the element name for the structure for writing to XML.
+	 * 
+	 * @return the element name for the structure for writing to XML.
+	 */
+	public abstract String getXMLElementName();
+
+    /**
+     * Write this structure out as an XML format string.
+     */
+    public String toXML() {
+
+        try {
+            StringWriter sWriter = new StringWriter();
+            XMLStreamWriter xmlWriter = XMLOutputFactory.newInstance().createXMLStreamWriter(sWriter);
+
+            toXML(xmlWriter);
+            return sWriter.toString();
+        }
+        catch (XMLStreamException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    /**
+     * Set the name of this object's XML element and also content type attribute if content is structure type.
+     */
+    protected void setXmlNames() {
+
+        // is this structure holding a structure type container?
+        boolean holdingStructureType = false;
+
+        // type of data held by this container type
+        DataType type = header.getDataType();
+        if (type == null) return;
+
+        switch (type) {
+            case CHAR8:
+                xmlElementName = "int8"; break;
+            case UCHAR8:
+                xmlElementName = "uint8"; break;
+            case SHORT16:
+                xmlElementName = "int16"; break;
+            case USHORT16:
+                xmlElementName = "uint16"; break;
+            case INT32:
+                xmlElementName = "int32"; break;
+            case UINT32:
+                xmlElementName = "uint32"; break;
+            case LONG64:
+                xmlElementName = "int64"; break;
+            case ULONG64:
+                xmlElementName = "uint64"; break;
+            case FLOAT32:
+                xmlElementName = "float32"; break;
+            case DOUBLE64:
+                xmlElementName = "float64"; break;
+            case CHARSTAR8:
+                xmlElementName = "string"; break;
+            case COMPOSITE:
+                xmlElementName = "composite"; break;
+
+            case UNKNOWN32:
+                holdingStructureType = true;
+                xmlContentAttributeName = "unknown32";
+                break;
+
+            case TAGSEGMENT:
+//            case ALSOTAGSEGMENT:
+                holdingStructureType = true;
+                xmlContentAttributeName = "tagsegment";
+                break;
+
+            case SEGMENT:
+            case ALSOSEGMENT:
+                holdingStructureType = true;
+                xmlContentAttributeName = "segment";
+                break;
+
+            case BANK:
+            case ALSOBANK:
+                holdingStructureType = true;
+                xmlContentAttributeName = "bank";
+                break;
+
+            default:
+                xmlElementName = "unknown"; break;
+        }
+
+        if (holdingStructureType) {
+            // Which container type are we (NOT what are we holding)?
+            // Because that determines our element name.
+            StructureType structType = getStructureType();
+            if (structType == StructureType.UNKNOWN32) {
+                xmlElementName = "unknown32";
+            }
+            else {
+                xmlElementName = getXMLElementName();
+            }
+        }
+
+    }
+
+    /**
+     * Set the indentation (string of spaces) for more pleasing XML output.
+     * @param s the indentation (string of spaces) for more pleasing XML output
+     */
+    protected void setXmlIndent(String s) {
+        xmlIndent = s;
+    }
+
+    /**
+     * Increase the indentation (string of spaces) of the XML output.
+     */
+    protected void increaseXmlIndent() {
+        xmlIndent += "   ";
+    }
+
+    /**
+     * Decrease the indentation (string of spaces) of the XML output.
+     */
+    protected void decreaseXmlIndent() {
+        xmlIndent = xmlIndent.substring(0, xmlIndent.length() - 3);
+    }
+
+    /**
+     * What is the byte order of this data?
+     * @return {@link ByteOrder#BIG_ENDIAN} or {@link ByteOrder#LITTLE_ENDIAN}
+     */
+    public ByteOrder getByteOrder() {
+        return byteOrder;
+    }
+
+    /**
+     * Set the byte order of this data. This method <b>cannot</b> be used to swap data.
+     * It is only used to describe the endianness of the rawdata contained.
+     * @param byteOrder {@link ByteOrder#BIG_ENDIAN} or {@link ByteOrder#LITTLE_ENDIAN}
+     */
+    public void setByteOrder(ByteOrder byteOrder) {
+        this.byteOrder = byteOrder;
+    }
+
+	/**
+	 * Is a byte swap required? This is java and therefore big endian. If data is
+     * little endian, then a swap is required.
+	 * 
+	 * @return <code>true</code> if byte swapping is required (data is little endian).
+	 */
+	public boolean isSwap() {
+		return byteOrder == ByteOrder.LITTLE_ENDIAN;
+	}
+
+	/**
+	 * Get the description from the name provider (dictionary), if there is one.
+	 * 
+	 * @return the description from the name provider (dictionary), if there is one. If not, return
+	 *         NameProvider.NO_NAME_STRING.
+	 */
+	@Override
+	public String getDescription() {
+		return NameProvider.getName(this);
+	}
+
+
+	/**
+	 * Obtain a string representation of the structure.
+	 * 
+	 * @return a string representation of the structure.
+	 */
+	@Override
+	public String toString() {
+		StructureType stype = getStructureType();
+		DataType dtype = header.getDataType();
+
+		String description = getDescription();
+		if (INameProvider.NO_NAME_STRING.equals(description)) {
+			description = null;
+		}
+		String s = stype + " of " + dtype + "s " + " len (ints): " + header.length + " " + " tag: " + header.tag;
+		if (this instanceof EvioBank) {
+			s += " num: " + header.number;
+		}
+
+        // todo: can be more thorough here for datalen calculation
+
+		if (rawBytes == null) {
+			s += " data: null";
+		}
+		else {
+			s += " datalen (bytes): " + rawBytes.length;
+		}
+
+		if (description != null) {
+			s += " [" + description + "]";
+		}
+
+		int numChildren = (children == null) ? 0 : children.size();
+
+		s += " <#children: " + numChildren + ">";
+		return s;
+	}
+
+	/**
+	 * This is a method from the IEvioStructure Interface. Return the header for this structure.
+	 * 
+	 * @return the header for this structure.
+	 */
+	@Override
+    public BaseStructureHeader getHeader() {
+		return header;
+	}
+
+    /**
+     * Get the number of stored data items like number of banks, ints, floats, etc.
+     * (not the size in ints or bytes). Some items may be padded such as shorts
+     * and bytes. This will tell the meaningful number of such data items.
+     * In the case of containers, returns number of bytes not in header.
+     *
+     * @return number of stored data items (not size or length),
+     *         or number of bytes if container
+     */
+    public int getNumberDataItems() {
+        if (isContainer()) {
+            numberDataItems = header.getLength() + 1 - header.getHeaderLength();
+        }
+
+        // if the calculation has not already been done ...
+        if (numberDataItems < 1) {
+            // When parsing a file or byte array, it is not fully unpacked until data
+            // is asked for specifically, for example as an int array or a float array.
+            // Thus we don't know how many of a certain item (say doubles) there is.
+            // But we can figure that out now based on the size of the raw data byte array.
+            int divisor = 0;
+            int padding = 0;
+            DataType type = header.getDataType();
+
+            switch (type) {
+                case CHAR8:
+                case UCHAR8:
+                    padding = header.getPadding();
+                    divisor = 1; break;
+                case SHORT16:
+                case USHORT16:
+                    padding = header.getPadding();
+                    divisor = 2; break;
+                case INT32:
+                case UINT32:
+                case FLOAT32:
+                    divisor = 4; break;
+                case LONG64:
+                case ULONG64:
+                case DOUBLE64:
+                    divisor = 8; break;
+                case CHARSTAR8:
+                    String[] s = getStringData();
+                    numberDataItems = s.length;
+                    break;
+                case COMPOSITE:
+                    // For this type, numberDataItems is NOT used to
+                    // calculate the data length so we're OK returning
+                    // any reasonable value here.
+                    numberDataItems = 1;
+                    if (compositeData != null) numberDataItems = compositeData.getItems().size();
+                    break;
+                default:
+            }
+
+            if (divisor > 0 && rawBytes != null) {
+                numberDataItems = (rawBytes.length - padding)/divisor;
+            }
+        }
+
+        return numberDataItems;
+    }
+
+    /**
+     * Get the length of this structure in bytes, including the header.
+     * @return the length of this structure in bytes, including the header.
+     */
+    public int getTotalBytes() {
+        return 4*(header.getLength() + 1);
+    }
+
+	/**
+	 * Get the raw data of the structure.
+	 * 
+	 * @return the raw data of the structure.
+	 */
+	public byte[] getRawBytes() {
+		return rawBytes;
+	}
+
+	/**
+	 * Set the data for the structure.
+	 * 
+	 * @param rawBytes the structure raw data.
+	 */
+	public void setRawBytes(byte[] rawBytes) {
+		this.rawBytes = rawBytes;
+	}
+
+	/**
+	 * This is a method from the IEvioStructure Interface. Gets the raw data as an integer array,
+     * if the content type as indicated by the header is appropriate.<p>
+     * NOTE: since Java does not have unsigned primitives, both INT32 and UINT32
+	 * data types will be returned as int arrays. The application will have to deal
+     * with reinterpreting signed ints that are negative as unsigned ints.
+	 * 
+	 * @return the data as an int array, or <code>null</code> if this makes no sense for the given content type.
+	 */
+	@Override
+	public int[] getIntData() {
+
+        switch (header.getDataType()) {
+            case INT32:
+            case UINT32:
+                if (intData == null) {
+                    if (rawBytes == null) {
+                        return null;
+                    }
+                    try {
+                        intData = ByteDataTransformer.toIntArray(rawBytes, byteOrder);
+                    }
+                    catch (EvioException e) {/* should not happen */}
+                }
+                return intData;
+            default:
+                return null;
+        }
+	}
+
+	/**
+	 * This is a method from the IEvioStructure Interface. Gets the raw data as a long array,
+     * if the content type as indicated by the header is appropriate.<p>
+     * NOTE: since Java does not have unsigned primitives, both LONG64 and ULONG64
+     * data types will be returned as long arrays. The application will have to deal
+     * with reinterpreting signed longs that are negative as unsigned longs.
+	 * 
+	 * @return the data as an long array, or <code>null</code> if this makes no sense for the given content type.
+	 */
+	@Override
+	public long[] getLongData() {
+
+        switch (header.getDataType()) {
+            case LONG64:
+            case ULONG64:
+                if (longData == null) {
+                    if (rawBytes == null) {
+                        return null;
+                    }
+                    try {
+                        longData = ByteDataTransformer.toLongArray(rawBytes, byteOrder);
+                    }
+                    catch (EvioException e) {/* should not happen */}
+                }
+                return longData;
+            default:
+                return null;
+        }
+	}
+
+	/**
+	 * This is a method from the IEvioStructure Interface. Gets the raw data as a float array,
+     * if the content type as indicated by the header is appropriate.
+	 * 
+	 * @return the data as an double array, or <code>null</code> if this makes no sense for the given contents type.
+	 */
+	@Override
+	public float[] getFloatData() {
+
+        switch (header.getDataType()) {
+            case FLOAT32:
+                if (floatData == null) {
+                    if (rawBytes == null) {
+                        return null;
+                    }
+                    try {
+                        floatData = ByteDataTransformer.toFloatArray(rawBytes, byteOrder);
+                    }
+                    catch (EvioException e) {/* should not happen */}
+                }
+                return floatData;
+            default:
+                return null;
+        }
+	}
+
+	/**
+	 * This is a method from the IEvioStructure Interface. Gets the raw data as a double array,
+     * if the content type as indicated by the header is appropriate.
+	 * 
+	 * @return the data as an double array, or <code>null</code> if this makes no sense for the given content type.
+	 */
+	@Override
+	public double[] getDoubleData() {
+
+        switch (header.getDataType()) {
+            case DOUBLE64:
+                if (doubleData == null) {
+                    if (rawBytes == null) {
+                        return null;
+                    }
+                    try {
+                        doubleData = ByteDataTransformer.toDoubleArray(rawBytes, byteOrder);
+                    }
+                    catch (EvioException e) {/* should not happen */}
+                }
+                return doubleData;
+            default:
+                return null;
+        }
+	}
+
+	/**
+	 * This is a method from the IEvioStructure Interface. Gets the raw data as a short array,
+     * if the contents type as indicated by the header is appropriate.<p>
+     * NOTE: since Java does not have unsigned primitives, both SHORT16 and USHORT16
+     * data types will be returned as short arrays. The application will have to deal
+     * with reinterpreting signed shorts that are negative as unsigned shorts.
+	 *
+	 * @return the data as an short array, or <code>null</code> if this makes no sense for the given contents type.
+	 */
+	@Override
+	public short[] getShortData() {
+
+        switch (header.getDataType()) {
+            case SHORT16:
+            case USHORT16:
+                if (shortData == null) {
+                    if (rawBytes == null) {
+                        return null;
+                    }
+                    try {
+                        shortData = ByteDataTransformer.toShortArray(rawBytes, header.getPadding(), byteOrder);
+                    }
+                    catch (EvioException e) {/* should not happen */}
+                }
+                return shortData;
+            default:
+                return null;
+        }
+	}
+
+    /**
+     * 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>
+     *
+     * @return the data as a CompositeData object, 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 {
+
+        switch (header.getDataType()) {
+            case COMPOSITE:
+                if (compositeData == null) {
+                    if (rawBytes == null) {
+                        return null;
+                    }
+                    compositeData = new CompositeData(rawBytes, byteOrder);
+                }
+                return compositeData;
+            default:
+                return null;
+        }
+    }
+
+	/**
+	 * This is a method from the IEvioStructure Interface. Gets the raw data as an byte array,
+     * if the contents type as indicated by the header is appropriate.<p>
+     * NOTE: since Java does not have unsigned primitives, CHAR8 and UCHAR8
+	 * data types will be returned as byte arrays. The application will have to deal
+     * with reinterpreting bytes as characters, if necessary.
+	 * 
+	 * @return the data as an byte array, or <code>null</code> if this makes no sense for the given contents type.
+	 */
+	@Override
+	public byte[] getByteData() {
+
+        switch (header.getDataType()) {
+// for now, NO access to raw data behind stored strings
+//            case CHARSTAR8:
+//                // CHARSTAR8 data is already padded as part if its format.
+//                // If appendStringData() was called, rawBytes was set so we're OK.
+//                return rawBytes;
+//
+            case CHAR8:
+            case UCHAR8:
+                if (charData == null) {
+                    if (rawBytes == null) {
+                        return null;
+                    }
+                    // Get rid of padding on end, if any.
+                    charData = new byte[rawBytes.length - header.getPadding()];
+                    System.arraycopy(rawBytes, 0, charData, 0, rawBytes.length - header.getPadding());
+                }
+                return charData;
+            default:
+                return null;
+        }
+	}
+
+    /**
+     * This is a method from the IEvioStructure Interface. Gets the raw data (ascii) as an
+     * array of String objects, if the contents type as indicated by the header is appropriate.
+     * For any other behavior, the user should retrieve the data as a byte array and
+     * manipulate it in the exact manner desired.<p>
+     * Originally, in evio versions 1, 2 and 3, only one string was stored. Recent changes allow
+     * an array of strings to be stored and retrieved. The changes are backwards compatible.
+     *
+     * The following is true about the string raw data format:
+     * <ul>
+     * <li>All strings are immediately followed by an ending null (0).
+     * <li>All string arrays are further padded/ended with at least one 0x4 valued ASCII
+     *     char (up to 4 possible).
+     * <li>The presence of 1 to 4 ending 4's distinguishes the recent string array version from
+     *     the original, single string version.
+     * <li>The original version string may be padded with anything after its ending null.
+     * <li>Ending non-printing ascii chars (value < 32, = 127) are not included in the string
+     *     since they are there for padding.
+     * </ul>
+     *
+     * @return the data as an array of String objects if DataType is CHARSTAR8, or <code>null</code>
+     *         if this makes no sense for the given type.
+     *
+     */
+    @Override
+    public String[] getStringData() {
+
+        switch (header.getDataType()) {
+        case CHARSTAR8:
+
+            if (stringsList != null) {
+                return stringsList.toArray(new String[stringsList.size()]);
+            }
+
+            if (stringData == null && rawBytes == null) {
+                return null;
+            }
+
+            unpackRawBytesToStrings();
+
+            return stringsList.toArray(new String[stringsList.size()]);
+
+        default:
+            return null;
+        }
+    }
+
+
+    /**
+     * Extract an array of strings from raw evio string data.
+     *
+     * @param rawBytes raw evio string data
+     * @param offset offset into raw data array
+     * @return array of Strings or null if processing error
+     */
+    static public String[] unpackRawBytesToStrings(byte[] rawBytes, int offset) {
+
+        if (rawBytes == null || offset < 0) return null;
+
+        int length = rawBytes.length - offset;
+        StringBuffer stringData = new StringBuffer();
+        try {
+            // stringData contains all elements of rawBytes
+            stringData = new StringBuffer(new String(rawBytes, offset, length, "US-ASCII"));
+        }
+        catch (UnsupportedEncodingException e) { /* will never happen */ }
+
+        // Each string is terminated with a null (char val = 0)
+        // and in addition, the end is padded by ASCII 4's (char val = 4).
+        // However, in the legacy versions of evio, there is only one
+        // null-terminated string and anything as padding. To accomodate legacy evio, if
+        // there is not an ending ASCII value 4, anything past the first null is ignored.
+        // After doing so, split at the nulls. Do not use the String
+        // method "split" as any empty trailing strings are unfortunately discarded.
+
+        boolean newVersion = true;
+        if (stringData.charAt(stringData.length() - 1) != '\004') {
+            newVersion = false;
+        }
+
+        char c;
+        LinkedList<String> stringsList = new LinkedList<String>();
+        LinkedList<Integer> nullIndexList = new LinkedList<Integer>();
+
+        for (int i=0; i < stringData.length(); i++) {
+            c = stringData.charAt(i);
+            if ( c == '\000' ) {
+                nullIndexList.add(i);
+                // only 1 null terminated string originally in evio v2,3
+                if (!newVersion) {
+                    break;
+                }
+            }
+            // 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 ) {
+                break;
+            }
+        }
+
+        // One thing to consider is in the old version, if there is no
+        // null at the end, just pretend like there is one.
+
+        int firstIndex=0;
+        for (int nullIndex : nullIndexList) {
+            stringsList.add(stringData.substring(firstIndex, nullIndex));
+            firstIndex = nullIndex + 1;
+        }
+
+        if (stringsList.size() < 1) return null;
+
+        String strs[] = new String[stringsList.size()];
+        stringsList.toArray(strs);
+
+        return strs;
+    }
+
+
+    /**
+     * Extract string data from rawBytes array. Make sure rawBytes is in the
+     * proper jevio 4.0 format.
+     */
+    private void unpackRawBytesToStrings() {
+
+        if (rawBytes == null) return;
+
+        try {
+            // stringData contains all elements of rawBytes
+            stringData = new StringBuffer(new String(rawBytes, "US-ASCII"));
+        }
+        catch (UnsupportedEncodingException e) { /* will never happen */ }
+
+        // Each string is terminated with a null (char val = 0)
+        // and in addition, the end is padded by ASCII 4's (char val = 4).
+        // However, in the legacy versions of evio, there is only one
+        // null-terminated string and anything as padding. To accomodate legacy evio, if
+        // there is not an ending ASCII value 4, anything past the first null is ignored.
+        // After doing so, split at the nulls. Do not use the String
+        // method "split" as any empty trailing strings are unfortunately discarded.
+
+        boolean newVersion = true;
+        if (stringData.charAt(stringData.length() - 1) != '\004') {
+            newVersion = false;
+        }
+
+        char c;
+        stringsList = new LinkedList<String>();
+        LinkedList<Integer> nullIndexList = new LinkedList<Integer>();
+
+        for (int i=0; i < stringData.length(); i++) {
+            c = stringData.charAt(i);
+            if ( c == '\000' ) {
+//System.out.println("  found null at i = " + i);
+                nullIndexList.add(i);
+                // only 1 null terminated string originally in evio v2,3
+                if (!newVersion) {
+//System.out.println("  evio version 1-3 string");
+                    break;
+                }
+            }
+            // 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 ) {
+//System.out.println("  found non-printing c = " + (int)c + " at i = " + i);
+                break;
+            }
+        }
+
+        // One thing to consider is in the old version, if there is no
+        // null at the end, just pretend like there is one.
+
+//System.out.println("  split into " + nullIndexList.size() + " strings");
+        int firstIndex=0;
+        for (int nullIndex : nullIndexList) {
+            stringsList.add(stringData.substring(firstIndex, nullIndex));
+//System.out.println("    add " + stringData.substring(firstIndex, nullIndex));
+            firstIndex = nullIndex + 1;
+        }
+
+        // set length of everything up to & including last null (not padding)
+        stringEnd = firstIndex;
+        stringData.setLength(stringEnd);
+//System.out.println("    string = " + stringData.toString());
+
+        // convert this old byte representation into the new one
+        if (!newVersion) {
+            // Add any necessary padding to 4 byte boundaries.
+            // IMPORTANT: There must be at least one '\004'
+            // character at the end. This distinguishes evio
+            // string array version from earlier version.
+            int[] pads = {4,3,2,1};
+            switch (pads[stringData.length()%4]) {
[truncated at 1000 lines; 1078 more skipped]

jevio-base/src/main/java/org/jlab/coda/jevio
BaseStructureHeader.java 1.2 -> 1.3
diff -N BaseStructureHeader.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ BaseStructureHeader.java	28 Feb 2012 19:41:35 -0000	1.3
@@ -0,0 +1,247 @@
+package org.jlab.coda.jevio;
+
+import java.nio.ByteOrder;
+
+/**
+ * This the  header for the base structure (<code>BaseStructure</code>). It does not contain the raw data, just the
+ * header. The three headers for the actual structures found in evio (BANK, SEGMENT, and TAGSEMENT) all extend this.
+ * 
+ * @author heddle
+ * 
+ */
+public abstract class BaseStructureHeader implements IEvioWriter {
+
+    /**
+     * The length of the structure in ints. This never includes the
+     * first header word itself (which contains the length).
+     */
+    protected int length;
+
+	/**
+	 * The structure tag.
+	 */
+	protected int tag = 0;
+
+    /**
+     * The data type of the structure.
+     */
+    protected DataType dataType;
+
+    /**
+     * The amount of padding bytes when storing short or byte data.
+     * 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;
+
+	/**
+	 * The number represents an unsigned byte. Only Banks have a number
+     * field in their header, so this is only relevant for Banks.
+	 */
+	protected int number;
+
+    /**
+	 * Null constructor.
+	 */
+	public BaseStructureHeader() {
+	}
+	
+	/**
+	 * Constructor
+	 * @param tag the tag for the header.
+	 * @param dataType the enum data type for the content of the structure.
+	 */
+	public BaseStructureHeader(int tag, DataType dataType) {
+        this(tag, dataType, 0);
+	}
+
+	/**
+	 * Constructor
+	 * @param tag the tag for the header.
+	 * @param dataType the data type for the content of the structure.
+	 * @param num sometimes, but not necessarily, an ordinal enumeration.
+	 */
+	public BaseStructureHeader(int tag, DataType dataType, int num) {
+		this.tag = tag;
+		this.dataType = dataType;
+		setNumber(num);
+	}
+
+
+	/**
+	 * Get the number. Only Banks have a number field in their header, so this is only relevant for Banks.
+	 * 
+	 * @return the number.
+	 */
+	public int getNumber() {
+		return number;
+	}
+
+	/**
+	 * Set the number. Only Banks have a number field in their header, so this is only relevant for Banks.
+	 * 
+	 * @param number the number.
+	 */
+	public void setNumber(int number) {
+		//num is really an unsigned byte
+		if (number < 0) {
+			number += 256;
+		}
+		this.number = number & 0xff;
+	}
+
+	/**
+	 * Get the data type for the structure.
+	 * 
+	 * @return the data type for the structure.
+	 */
+	public int getDataTypeValue() {
+		return dataType.getValue();
+	}
+
+    /**
+     * Set the numeric data type for the structure.
+     *
+     * @param dataType the dataTtype for the structure.
+     */
+    void setDataType(int dataType) {
+        this.dataType = DataType.getDataType(dataType);
+    }
+
+    /**
+     * Set the numeric data type for the structure.
+     *
+     * @param dataType the dataTtype for the structure.
+     */
+    public void setDataType(DataType dataType) {
+        this.dataType = dataType;
+    }
+
+    /**
+     * Returns the data type for data stored in this structure as a <code>DataType</code> enum.
+     *
+     * @return the data type for data stored in this structure as a <code>DataType</code> enum.
+     * @see DataType
+     */
+    public DataType getDataType() {
+        return dataType;
+    }
+
+    /**
+     * Returns the data type as a string.
+     *
+     * @return the data type as a string.
+     */
+    public String getDataTypeName() {
+        return dataType.name();
+    }
+
+    /**
+     * Get the amount of padding bytes when storing short or byte data.
+     * Value is 0, 1, 2, or 3 for bytes and 0 or 2 for shorts.
+     * @return
+     */
+    public int getPadding() {
+        return padding;
+    }
+
+    /**
+     * Set the amount of padding bytes when storing short or byte data.
+     * Allowed value is 0, 1, 2, or 3 for bytes and 0 or 2 for shorts.
+     * @param padding amount of padding bytes when storing short or byte data (0-3).
+     */
+    protected void setPadding(int padding) {
+        this.padding = padding;
+    }
+
+    /**
+     * Get the length of the structure in ints, not counting the length word.
+     *
+     * @return Get the length of the structure in ints (not counting the length word).
+     */
+    public int getLength() {
+        return length;
+    }
+
+    /**
+     * Set the length of the structure in ints, not counting the length word.
+     *
+     * @param length the length of the structure in ints, not counting the length word.
+     */
+    public void setLength(int length) {
+        this.length = length;
+    }
+
+    /**
+     * Get the length of the structure's header in ints. This includes the first header word itself
+     * (which contains the length) and in the case of banks, it also includes the second header word.
+     *
+     * @return Get the length of the structure's header in ints.
+     */
+    public abstract int getHeaderLength();
+
+    /**
+     * Write myself out as a byte array of evio format data
+     * into the given byte array in the specified byte order.
+     *
+     * @return byte array containing evio format data of this bank in currently set byte order
+     */
+    protected abstract void toArray(byte[] bArray, int offset, ByteOrder order);
+
+	/**
+	 * Get the structure tag.
+	 * 
+	 * @return the structure tag.
+	 */
+	public int getTag() {
+		return tag;
+	}
+
+	/**
+	 * Set the structure tag.
+	 * 
+	 * @param tag the structure tag.
+	 */
+	public void setTag(int tag) {
+		this.tag = tag;
+	}
+
+	/**
+	 * Obtain a string representation of the structure header.
+	 * 
+	 * @return a string representation of the structure header.
+	 */
+	@Override
+	public String toString() {
+		StringBuffer sb = new StringBuffer(512);
+		sb.append(String.format("structure length: %d\n", length));
+		sb.append(String.format("data type:   %s\n", getDataTypeName()));
+        sb.append(String.format("tag:         %d\n", tag));
+        sb.append(String.format("padding:     %d\n", padding));
+		return sb.toString();
+	}
+	
+	/**
+	 * Convenience method to return the byte value of an integer. Although
+	 * the parameter is an Integer, use "autoboxing" to pass in a primitive. I.e.,
+	 * byteValue(3) works just fine.
+	 * @param integer the integer whose byte value is needed. Can pass in a primitive int.
+	 * @return the byte value of the integer.
+	 */
+	public byte byteValue(Integer integer) {
+		return integer.byteValue();
+	}
+	
+	/**
+	 * Convenience method to return the short value of an integer. Although
+	 * the parameter is an Integer, use "autoboxing" to pass in a primitive. I.e.,
+	 * shortValue(3345) works just fine.
+	 * @param integer the integer whose short value is needed. Can pass in a primitive int.
+	 * @return the short value of the integer.
+	 */
+	public short shortValue(Integer integer) {
+		return integer.shortValue();
+	}
+
+
+}

jevio-base/src/main/java/org/jlab/coda/jevio
BlockHeaderV2.java added at 1.1
diff -N BlockHeaderV2.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ BlockHeaderV2.java	28 Feb 2012 19:41:36 -0000	1.1
@@ -0,0 +1,501 @@
+package org.jlab.coda.jevio;
+
+import java.nio.ByteBuffer;
+
+/**
+ * This holds a evio block header, also known as a physical record header.
+ * Unfortunately, in versions 1, 2 & 3, evio files impose an anachronistic
+ * block structure. The complication that arises is that logical records
+ * (events) will sometimes cross physical record boundaries.
+ *
+ *
+ * <code><pre>
+ * ####################################
+ * Evio block header, versions 1,2 & 3:
+ * ####################################
+ *
+ * MSB(31)                          LSB(0)
+ * <---  32 bits ------------------------>
+ * _______________________________________
+ * |            Block Length             |
+ * |_____________________________________|
+ * |            Block Number             |
+ * |_____________________________________|
+ * |          Header Length = 8          |
+ * |_____________________________________|
+ * |               Start                 |
+ * |_____________________________________|
+ * |                End                  |
+ * |_____________________________________|
+ * |              Version                |
+ * |_____________________________________|
+ * |             Reserved 1              |
+ * |_____________________________________|
+ * |            Magic Number             |
+ * |_____________________________________|
+ *
+ *
+ *      Block Length  = number of ints in block (including this one).
+ *                      This is fixed for versions 1-3, generally at 8192 (32768 bytes)
+ *      Block Number  = id number
+ *      Header Length = number of ints in this header (always 8)
+ *      Start         = offset to first event header in block relative to start of block
+ *      End           = # of valid words (header + data) in block (normally = block size)
+ *      Version       = evio format version
+ *      Reserved 1    = reserved
+ *      Magic #       = magic number (0xc0da0100) used to check endianness
+ *
+ * </pre></code>
+ *
+ *
+ * @author heddle
+ *
+ */
+public class BlockHeaderV2 implements IEvioWriter, IBlockHeader {
+
+	/**
+	 * The maximum block size in 32 bit ints in this implementation of evio.
+     * There is, in actuality, no limit on size; however, the versions 1-3 C
+     * library only used 8192 as the block size.
+	 */
+	public static final int MAX_BLOCK_SIZE = 32768;
+
+	/**
+	 * The block (physical record) size in 32 bit ints.
+	 */
+	private int size;
+
+	/**
+	 * The block number. In a file, this is usually sequential.
+	 */
+	private int number;
+
+	/**
+	 * The block header length. Should be 8 in all cases, so getting this correct constitutes a check.
+	 */
+	private int headerLength;
+
+	/**
+     * Offset (in ints, relative to start of block) to the start of the first event (logical record) that begins in this
+     * block. For the first event it will just be = 8, the size of the block header. For subsequent physical records it
+     * will generally not be 8. Note that a logical record (event) that spans three blocks (physical records) will have
+     * <code>start = 0</code>.
+     *
+	 */
+	private int start;
+
+	/**
+	 * The number of valid words (header + data) in the block (physical record.) This is normally the same as the block
+	 * size, except for the last block (physical record) in the file. <br>
+	 * NOTE: for evio files, even if end < size (blocksize) for the last block (physical record), the data behind it
+	 * will be padded with zeroes so that the file size is an integer multiple of the block size.
+	 */
+	private int end;
+
+	/**
+	 * The evio version.
+	 */
+	private int version;
+
+	/**
+	 * First reserved word. Sometimes this is used to indicate the ordinal number of the last event that starts
+	 * within this block--but that is not mandated. In that case, if the previous block had a value of
+	 * reserved1 = 6 and this block has a value of 9, then this block contains the end of event 6, all of events
+	 * 7 and 8, and the start of event 9--unless it ends exactly on the end of event 8.<br>
+	 */
+	private int reserved1;
+
+	/**
+	 * This is the magic word: 0xc0da0100 (formerly reserved2). Used to check endianness.
+	 */
+	private int magicNumber;
+
+	/**
+	 * This is not part of the block header proper. It is a position in a memory buffer of the start of the block
+	 * (physical record). It is kept for convenience.
+	 */
+	private int bufferStartingPosition = -1;
+
+    /**
+	 * Get the size of the block (physical record).
+	 *
+	 * @return the size of the block (physical record) in ints.
+	 */
+	public int getSize() {
+		return size;
+	}
+
+	/**
+	 * Null constructor initializes all fields to zero.
+	 */
+	public BlockHeaderV2() {
+		size = 0;
+		number = 0;
+		headerLength = 0;
+		start = 0;
+		end = 0;
+		version = 0;
+		reserved1 = 0;
+		magicNumber = 0;
+	}
+
+    /**
+     * Creates a BlockHeader for evio versions 1-3 format. Only the <code>block size</code>
+     * and <code>block number</code> are provided. The other six words, which can be
+     * modified by setters, are initialized to these values:<br>
+     *<ul>
+     *<li><code>headerLength</code> is initialized to 8<br>
+     *<li><code>start</code> is initialized to 8<br>
+     *<li><code>end</code> is initialized to <code>size</code><br>
+     *<li><code>version</code> is initialized to 2<br>
+     *<li><code>reserved1</code> is initialized to 0<br>
+     *<li><code>magicNumber</code> is initialized to <code>MAGIC_NUMBER</code><br>
+     *</ul>
+     * @param size the size of the block in ints.
+     * @param number the block number--usually sequential.
+     */
+    public BlockHeaderV2(int size, int number) {
+        this.size = size;
+        this.number = number;
+        headerLength = 8;
+        start = 8;
+        end = size;
+        version = 2;
+        reserved1 = 0;
+        magicNumber = MAGIC_NUMBER;
+    }
+
+
+    /**
+     * Gets whether this block's first event is an evio dictionary.
+     * This is not implemented in evio versions 1-3. Just return false.
+     *
+     * @return always returns false for evio versions 1-3
+     */
+    public boolean hasDictionary() {
+        return false;
+    }
+
+    /**
+     * Is this the last block in the file or being sent over the network?
+     * This is not implemented in evio versions 1-3. Just return false.
+     *
+     * @return always returns false for evio versions 1-3
+     */
+    public boolean isLastBlock() {
+        return false;
+    }
+
+
+	/**
+	 * Set the size of the block (physical record). Some trivial checking is done.
+	 *
+	 * @param size the new value for the size, in ints.
+	 * @throws org.jlab.coda.jevio.EvioException
+	 */
+	public void setSize(int size) throws EvioException {
+        if ((size < 8) || (size > MAX_BLOCK_SIZE)) {
+			throw new EvioException(String.format("Bad value for size in block (physical record) header: %d", size));
+		}
+
+        // I'm not sure why this restriction is in here - timmer
+        if ((size % 256) != 0) {
+            throw new EvioException(String.format(
+                    "Bad value for size in block (physical record) header: %d (must be multiple of 256 ints)", size));
+        }
+        this.size = size;
+	}
+
+
+    /**
+     * Get the starting position of the block (physical record.). This is the offset (in ints, relative to start of
+     * block) to the start of the first event (logical record) that begins in this block. For the first event it will
+     * just be = 8, the size of the block header. For subsequent blocks it will generally not be 8. Note that a
+     * an event that spans three blocks (physical records) will have <code>start = 0</code>.
+     *
+     * NOTE: a logical record (event) that spans three blocks (physical records) will have <code>start = 0</code>.
+     *
+     * @return the starting position of the block (physical record.)
+     */
+    public int getStart() {
+        return start;
+    }
+
+    /**
+     * Set the starting position of the block (physical record.). This is the offset (in ints, relative to start of
+     * block) to the start of the first event (logical record) that begins in this block. For the first event it will
+     * just be = 8, the size of the block header. For subsequent blocks it will generally not be 8. Some trivial
+     * checking is done. Note that an event that spans three blocks (physical records) will have
+     * <code>start = 0</code>.
+     *
+     * NOTE: a logical record (event) that spans three blocks (physical records) will have <code>start = 0</code>.
+     *
+     * @param start the new value for the start.
+     * @throws EvioException
+     */
+    public void setStart(int start) throws EvioException {
+        if ((start < 0) || (start > MAX_BLOCK_SIZE)) {
+            throw new EvioException(String.format("Bad value for start in block (physical record) header: %d", start));
+        }
+        this.start = start;
+    }
+
+	/**
+	 * Get the ending position of the block (physical record.) This is the number of valid words (header + data) in the
+	 * block (physical record.) This is normally the same as the block size, except for the last block (physical record)
+	 * in the file.<br>
+	 * NOTE: for evio files, even if end < size (blocksize) for the last block (physical record), the data behind it
+	 * will be padded with zeroes so that the file size is an integer multiple of the block size.
+	 *
+	 * @return the ending position of the block (physical record.)
+	 */
+	public int getEnd() {
+		return end;
+	}
+
+	/**
+	 * Set the ending position of the block (physical record.) This is the number of valid words (header + data) in the
+	 * block (physical record.) This is normally the same as the block size, except for the last block (physical record)
+	 * in the file. Some trivial checking is done.<br>
+	 * NOTE: for evio files, even if end < size (blocksize) for the last block (physical record), the data behind it
+	 * will be padded with zeroes so that the file size is an integer multiple of the block size.
+	 *
+	 * @param end the new value for the end.
+	 * @throws EvioException
+	 */
+	public void setEnd(int end) throws EvioException {
+		if ((end < 8) || (end > MAX_BLOCK_SIZE)) {
+			throw new EvioException(String.format("Bad value for end in block (physical record) header: %d", end));
+		}
+		this.end = end;
+	}
+
+	/**
+	 * Get the block number for this block (physical record). In a file, this is usually sequential.
+	 *
+	 * @return the block number for this block (physical record).
+	 */
+	public int getNumber() {
+		return number;
+	}
+
+	/**
+	 * Set the block number for this block (physical record). In a file, this is usually sequential. This is not
+	 * checked.
+	 *
+	 * @param number the number of the block (physical record).
+	 */
+	public void setNumber(int number) {
+		this.number = number;
+	}
+
+	/**
+	 * Get the block header length, in ints. This should be 8.
+	 *
+	 * @return the block header length. This should be 8.
+	 */
+	public int getHeaderLength() {
+		return headerLength;
+	}
+
+	/**
+	 * Set the block header length, in ints. This should be 8. However, since this is usually read as part of reading
+	 * the physical record header, it is a good check to have a setter rather than just fix its value at 8.
+	 *
+	 * param headerLength the new block header length. This should be 8.
+	 *
+	 * @throws EvioException if headerLength is not 8.
+	 */
+	public void setHeaderLength(int headerLength) throws EvioException {
+		if (headerLength != 8) {
+			String message = "Bad Block (Physical Record) Header Length: " + headerLength;
+			throw new EvioException(message);
+		}
+		this.headerLength = headerLength;
+	}
+
+	/**
+	 * Get the evio version of the block (physical record) header.
+	 *
+	 * @return the evio version of the block (physical record) header.
+	 */
+	public int getVersion() {
+		return version;
+	}
+
+    /**
+     * Sets the evio version. Should be 1, 2 or 3 but no check is performed here, see
+     * {@link EvioFile#nextBlockHeader()}.
+     *
+     * @param version the evio version of evio.
+     */
+    public void setVersion(int version) {
+        this.version = version;
+    }
+
+	/**
+	 * Get the first reserved word in the block (physical record) header. Used in evio versions 1-3 only.
+	 *
+	 * @return the first reserved word in the block (physical record). Used in evio versions 1-3 only.
+	 */
+	public int getReserved1() {
+		return reserved1;
+	}
+
+	/**
+	 * Sets the value of reserved 1.
+	 *
+	 * @param reserved1 the value for reserved1.
+	 */
+	public void setReserved1(int reserved1) {
+		this.reserved1 = reserved1;
+	}
+
+	/**
+	 * Get the magic number the block (physical record) header which should be 0xc0da0100.
+     * Formerly this location in the header was called "reserved2".
+	 *
+	 * @return the magic number in the block (physical record).
+	 */
+	public int getMagicNumber() {
+		return magicNumber;
+	}
+
+	/**
+	 * Sets the value of magicNumber. This should match the constant MAGIC_NUMBER.
+     * If it doesn't, some obvious possibilities: <br>
+	 * 1) The evio data (perhaps from a file) is screwed up.<br>
+	 * 2) The reading algorithm is screwed up. <br>
+	 * 3) The endianess is not being handled properly.
+	 *
+	 * @param magicNumber the new value for magic number.
+	 * @throws EvioException
+	 */
+	public void setMagicNumber(int magicNumber) throws EvioException {
+		if (magicNumber != MAGIC_NUMBER) {
+			throw new EvioException(String.format("Value for magicNumber %8x does not match MAGIC_NUMBER 0xc0da0100.",
+                                                  magicNumber));
+		}
+		this.magicNumber = magicNumber;
+	}
+
+	/**
+	 * Obtain a string representation of the block (physical record) header.
+	 *
+	 * @return a string representation of the block (physical record) header.
+	 */
+	@Override
+	public String toString() {
+		StringBuffer sb = new StringBuffer(512);
+		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("start:         %d\n", start));
+		sb.append(String.format("end:           %d\n", end));
+		sb.append(String.format("version:       %d\n", version));
+		sb.append(String.format("reserved1:     %d\n", reserved1));
+		sb.append(String.format("magicNumber:   %8x\n", magicNumber));
+		sb.append(String.format(" *buffer start:  %d\n", getBufferStartingPosition()));
+		sb.append(String.format(" *next   start:  %d\n", nextBufferStartingPosition()));
+		return sb.toString();
+	}
+
+    /**
+     * Get the position in the buffer (in bytes) of this block's last data word.<br>
+     *
+     * @return the position in the buffer (in bytes) of this block's last data word.
+     */
+    public int getBufferEndingPosition() {
+        return bufferStartingPosition + 4*end;
+    }
+
+    /**
+     * Get the starting position in the buffer (in bytes) from which this header was read--if that happened.<br>
+     * This is not part of the block header proper. It is a position in a memory buffer of the start of the block
+     * (physical record). It is kept for convenience. It is up to the reader to set it.
+     *
+     * @return the starting position in the buffer (in bytes) from which this header was read--if that happened.
+     */
+    public int getBufferStartingPosition() {
+        return bufferStartingPosition;
+    }
+
+	/**
+	 * Set the starting position in the buffer (in bytes) from which this header was read--if that happened.<br>
+	 * This is not part of the block header proper. It is a position in a memory buffer of the start of the block
+	 * (physical record). It is kept for convenience. It is up to the reader to set it.
+	 *
+	 * @param bufferStartingPosition the starting position in the buffer from which this header was read--if that
+	 *            happened.
+	 */
+	public void setBufferStartingPosition(int bufferStartingPosition) {
+		this.bufferStartingPosition = bufferStartingPosition;
+	}
+
+	/**
+	 * Determines where the start of the next block (physical record) header in some buffer is located (in bytes).
+     * This assumes the start position has been maintained by the object performing the buffer read.
+	 *
+	 * @return the start of the next block (physical record) header in some buffer is located (in bytes).
+	 */
+	public int nextBufferStartingPosition() {
+		return bufferStartingPosition + 4*size;
+	}
+
+	/**
+	 * Determines where the start of the first event (logical record) in this block (physical record) is located
+     * (in bytes). This assumes the start position has been maintained by the object performing the buffer read.
+	 *
+	 * @return where the start of the first event (logical record) in this block (physical record) is located
+     *         (in bytes). Returns 0 if start is 0, signaling that this entire physical record is part of a
+     *         logical record that spans at least three physical records.
+	 */
+	public int firstEventStartingPosition() {
+        if (start == 0) {
+			return 0;
+		}
+		return bufferStartingPosition + 4*start;
+	}
+
+	/**
+	 * 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.
+	 *
+	 * @param position the absolute current position is a byte buffer.
+	 * @return the number of bytes remaining in this block (physical record.)
+	 * @throws EvioException
+	 */
+	public int bytesRemaining(int position) throws EvioException {
+		if (position < bufferStartingPosition) {
+			throw new EvioException("Provided position is less than buffer starting position.");
+		}
+
+		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.");
+		}
+
+		return nextBufferStart - position;
+	}
+
+	/**
+	 * Write myself out a byte buffer. This write is relative--i.e., it uses the current position of the buffer.
+	 *
+	 * @param byteBuffer the byteBuffer to write to.
+	 * @return the number of bytes written, which for a BlockHeader is 32.
+	 */
+	@Override
+	public int write(ByteBuffer byteBuffer) {
+		byteBuffer.putInt(size);
+		byteBuffer.putInt(number);
+		byteBuffer.putInt(headerLength); // should always be 8
+		byteBuffer.putInt(start);
+		byteBuffer.putInt(end);
+		byteBuffer.putInt(version);
+		byteBuffer.putInt(reserved1);
+		byteBuffer.putInt(magicNumber);
+		return 32;
+	}
+}
\ No newline at end of file

jevio-base/src/main/java/org/jlab/coda/jevio
BlockHeaderV4.java added at 1.1
diff -N BlockHeaderV4.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ BlockHeaderV4.java	28 Feb 2012 19:41:36 -0000	1.1
@@ -0,0 +1,527 @@
+package org.jlab.coda.jevio;
+
+import java.nio.ByteBuffer;
+import java.util.BitSet;
+
+/**
+ * This holds a evio block header, also known as a physical record header.
+ * Unfortunately, in versions 1, 2 & 3, evio files impose an anachronistic
+ * block structure. The complication that arises is that logical records
+ * (events) will sometimes cross physical record boundaries. This block structure
+ * is changed in version 4 so that blocks only contain integral numbers of events.
+ * The information stored in this block header has also changed.
+ *
+ *
+ * <code><pre>
+ * ################################
+ * Evio block header, version 4:
+ * ################################
+ *
+ * MSB(31)                          LSB(0)
+ * <---  32 bits ------------------------>
+ * _______________________________________
+ * |            Block Length             |
+ * |_____________________________________|
+ * |            Block Number             |
+ * |_____________________________________|
+ * |          Header Length = 8          |
+ * |_____________________________________|
+ * |             Event Count             |
+ * |_____________________________________|
+ * |           unused/reserved           |
+ * |_____________________________________|
+ * |          Bit info         | Version |
+ * |_____________________________________|
+ * |           unused/reserved           |
+ * |_____________________________________|
+ * |             Magic Int               |
+ * |_____________________________________|
+ *
+ *
+ *      Block Length       = number of ints in block (including this one).
+ *      Block Number       = id number
+ *      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
+ *                           events since the first block may have a dictionary whose
+ *                           presence is not included in this count.
+ *      Bit info & Version = Lowest 8 bits are the version number (4).
+ *                           Upper 24 bits contain bit info.
+ *                           If a dictionary is included as the first event, bit #9 is set (=1)
+ *      Magic Int          = magic number (0xc0da0100) used to check endianness
+ *
+ *
+ *
+ * Bit info has the first (lowest) 2 bits taken:
+ *   Bit 0 = true if dictionary is included (relevant for first block only)
+ *   Bit 1 = true if this block is the last block in file or network transmission
+ *
+ *
+ *
+ * </pre></code>
+ *
+ *
+ * @author heddle
+ * @author timmer
+ *
+ */
+public class BlockHeaderV4 implements IEvioWriter, IBlockHeader {
+
+	/**
+	 * The maximum block size in 32 bit ints.
+     * Don't allow more than 40MBytes per block.
+	 */
+	public static final int MAX_BLOCK_SIZE = 10000000;
+
+	/** The block (physical record) size in 32 bit ints. */
+	private int size;
+
+	/** The block number. In a file, this is usually sequential. */
+	private int number;
+
+	/** The block header length which is always 8. */
+	private int headerLength;
+
+    /**
+     * Since blocks only contain whole events in this version,
+     * this stores the number of events contained in a block.
+     */
+    private int eventCount;
+
+	/** The evio version which is always 4. */
+	private int version;
+
+    /** Bit information. Bit one: is the first event a dictionary? */
+    private BitSet bitInfo = new BitSet(24);
+;
+
+	/** This is the magic word, 0xc0da0100, used to check endianness. */
+	private int magicNumber;
+
+	/**
+	 * This is not part of the block header proper. It is a position in a memory buffer
+     * of the start of the block (physical record). It is kept for convenience.
+	 */
+	private int bufferStartingPosition = -1;
+
+    /**
+	 * Get the size of the block (physical record).
+	 *
+	 * @return the size of the block (physical record) in ints.
+	 */
+	public int getSize() {
+		return size;
+	}
+
+	/**
+	 * Null constructor initializes all fields to zero.
+	 */
+	public BlockHeaderV4() {
+		size = 0;
+		number = 0;
+		headerLength = 0;
+		version = 0;
+		eventCount = 0;
+		magicNumber = 0;
+	}
+
+    /**
+     * Creates a BlockHeader for evio version 4 format. Only the <code>block size</code>
+     * and <code>block number</code> are provided. The other six words, which can be
+     * modified by setters, are initialized to these values:<br>
+     *<ul>
+     *<li><code>headerLength</code> is initialized to 8<br>
+     *<li><code>start</code> is initialized to 8<br>
+     *<li><code>end</code> is initialized to <code>size</code><br>
+     *<li><code>version</code> is initialized to 4<br>
+     *<li><code>bitInfo</code> is initialized to all bits off<br>
+     *<li><code>magicNumber</code> is initialized to <code>MAGIC_NUMBER</code><br>
+     *</ul>
+     * @param size the size of the block in ints.
+     * @param number the block number--usually sequential.
+     */
+    public BlockHeaderV4(int size, int number) {
+        this.size = size;
+        this.number = number;
+        headerLength = 8;
+        version = 4;
+        eventCount = 0;
+        magicNumber = MAGIC_NUMBER;
+    }
+
+	/**
+	 * Set the size of the block (physical record). Some trivial checking is done.
+	 *
+	 * @param size the new value for the size, in ints.
+	 * @throws EvioException
+	 */
+	public void setSize(int size) throws EvioException {
+        if ((size < 8) || (size > MAX_BLOCK_SIZE)) {
+			throw new EvioException(String.format("Bad value for size in block (physical record) header: %d", size));
+		}
+
+        this.size = size;
+	}
+
+    /**
+     * Get the number of events completely contained in the block.
+     * NOTE: There are no partial events, only complete events stored in one block.
+     *
+     * @return the number of events in the block.
+     */
+    public int getEventCount() {
+        return eventCount;
+    }
+
+    /**
+     * Set the number of events completely contained in the block.
+     * NOTE: There are no partial events, only complete events stored in one block.
+     *
+     * @param count the new number of events in the block.
+     * @throws EvioException
+     */
+    public void setEventCount(int count) throws EvioException {
+        if (count < 0) {
+            throw new EvioException(String.format("Bad value for event count in block (physical record) header: %d", count));
+        }
+        this.eventCount = count;
+    }
+
+	/**
+	 * Get the block number for this block (physical record). In a file, this is usually sequential.
+	 *
+	 * @return the block number for this block (physical record).
+	 */
+	public int getNumber() {
+		return number;
+	}
+
+	/**
+	 * Set the block number for this block (physical record).
+     * In a file, this is usually sequential. This is not
+	 * checked.
+	 *
+	 * @param number the number of the block (physical record).
+	 */
+	public void setNumber(int number) {
+		this.number = number;
+	}
+
+	/**
+	 * Get the block header length, in ints. This should be 8.
+	 *
+	 * @return the block header length. This should be 8.
+	 */
+	public int getHeaderLength() {
+		return headerLength;
+	}
+
+	/**
+	 * Set the block header length, in ints. Although technically speaking this value
+     * is variable, it should be 8.
+	 *
+	 * param headerLength the new block header length. This should be 8.
+	 */
+	public void setHeaderLength(int headerLength) {
+		if (headerLength != 8) {
+            System.out.println("Warning: Block Header Length = " + headerLength);
+		}
+		this.headerLength = headerLength;
+	}
+
+    /**
+     * Get the evio version of the block (physical record) header.
+     *
+     * @return the evio version of the block (physical record) header.
+     */
+    public int getVersion() {
+        return version;
+    }
+
+    /**
+     * Sets the evio version. Should be 4 but no check is performed here, see
+     * {@link EvioFile#nextBlockHeader()}.
+     *
+     * @param version the evio version of evio.
+     */
+    public void setVersion(int version) {
+        this.version = version;
+    }
+
+    /**
+     * 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>
+     */
+    public boolean hasDictionary() {
+        return bitInfo.get(0);
+    }
+
+    /**
+     * Is this the last block in the file or being sent over the network?
+     *
+     * @return <code>true</code> if this is the last block in the file or being sent
+     *         over the network, else <code>false</code>
+     */
+    public boolean isLastBlock() {
+        return bitInfo.get(1);
+    }
+
+    /**
+     * Gets a copy of all stored bit information.
+     *
+     * @return copy of BitSet containing all stored bit information.
+     */
+    public BitSet getBitInfo() {
+        return (BitSet)bitInfo.clone();
+    }
+
+    /**
+     * Gets the value of a particular bit in the bitInfo field.
+     *
+     * @param bitIndex index of bit to get
+     * @return BitSet containing all stored bit information.
+     */
+    public boolean getBitInfo(int bitIndex) {
+        if (bitIndex < 0 || bitIndex > 23) {
+            return false;
+        }
+        return bitInfo.get(bitIndex);
+    }
+
+    /**
+     * Sets a particular bit in the bitInfo field.
+     *
+     * @param bitIndex index of bit to change
+     * @param value value to set bit to
+     */
+    public void setBit(int bitIndex, boolean value) {
+        if (bitIndex < 0 || bitIndex > 23) {
+            return;
+        }
+        bitInfo.set(bitIndex, value);
+    }
+
+    /**
+     * Calculates the sixth word of this header which has the version number
+     * in the lowest 8 bits and the bit info in the highest 24 bits.
+     *
+     * @return sixth word of this header.
+     */
+    public int getSixthWord() {
+        int v = version & 0xff;
+
+        for (int i=0; i < bitInfo.length(); i++) {
+            if (bitInfo.get(i)) {
+                v |= (0x1 << (8+i));
+            }
+        }
+
+        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.
+     *
+     * @param set Bitset containing all bits to be set?
+     * @return generated sixth word of this header.
+     */
+    static public int generateSixthWord(BitSet set) {
+        int v = 4; // version
+
+        for (int i=0; i < set.length(); i++) {
+            if (i > 23) {
+                break;
+            }
+            if (set.get(i)) {
+                v |= (0x1 << (8+i));
+            }
+        }
+
+        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.
+     *
+     * @param set 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) {
+        int v = 4; // version
+
+        for (int i=0; i < set.length(); i++) {
+            if (i > 23) {
+                break;
+            }
+            if (set.get(i)) {
+                v |= (0x1 << (8+i));
+            }
+        }
+
+        v =  isDictionary ? (v | 0x100) : v;
+        v =  isEnd ? (v | 0x200) : v;
+        return v;
+    }
+
+    /**
+     * Parses the argument into the bit info fields.
+     * This ignores the version in the lowest 8 bits.
+     *
+     * @return parses the sixth word of this header into the bit info.
+     */
+    public void parseToBitInfo(int word) throws EvioException {
+        for (int i=0; i < 24; i++) {
+            bitInfo.set(i, ((word >>> 8+i) & 0x1) > 0);
+        }
+    }
+
+	/**
+	 * Get the magic number the block (physical record) header which should be 0xc0da0100.
+	 *
+	 * @return the magic number in the block (physical record).
+	 */
+	public int getMagicNumber() {
+		return magicNumber;
+	}
+
+	/**
+	 * Sets the value of magicNumber. This should match the constant MAGIC_NUMBER.
+     * If it doesn't, some obvious possibilities: <br>
+	 * 1) The evio data (perhaps from a file) is screwed up.<br>
+	 * 2) The reading algorithm is screwed up. <br>
+	 * 3) The endianess is not being handled properly.
+	 *
+	 * @param magicNumber the new value for magic number.
+	 * @throws EvioException
+	 */
+	public void setMagicNumber(int magicNumber) throws EvioException {
+		if (magicNumber != MAGIC_NUMBER) {
+			throw new EvioException(String.format("Value for magicNumber %8x does not match MAGIC_NUMBER 0xc0da0100.",
+                                                  magicNumber));
+		}
+		this.magicNumber = magicNumber;
+	}
+
+	/**
+	 * Obtain a string representation of the block (physical record) header.
+	 *
+	 * @return a string representation of the block (physical record) header.
+	 */
+	@Override
+	public String toString() {
+		StringBuffer sb = new StringBuffer(512);
+		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("has dictionary: %b\n", hasDictionary()));
+        sb.append(String.format("version:        %d\n", version));
+		sb.append(String.format("magicNumber:    %8x\n", magicNumber));
+		sb.append(String.format(" *buffer start: %d\n", getBufferStartingPosition()));
+		sb.append(String.format(" *next   start: %d\n", nextBufferStartingPosition()));
+		return sb.toString();
+	}
+
+    /**
+     * Get the position in the buffer (in bytes) of this block's last data word.<br>
+     *
+     * @return the position in the buffer (in bytes) of this block's last data word.
+     */
+    public int getBufferEndingPosition() {
+        return bufferStartingPosition + 4*size;
+    }
+
+    /**
+     * Get the starting position in the buffer (in bytes) from which this header was read--if that happened.<br>
+     * This is not part of the block header proper. It is a position in a memory buffer of the start of the block
+     * (physical record). It is kept for convenience. It is up to the reader to set it.
+     *
+     * @return the starting position in the buffer (in bytes) from which this header was read--if that happened.
+     */
+    public int getBufferStartingPosition() {
+        return bufferStartingPosition;
+    }
+
+	/**
+	 * Set the starting position in the buffer (in bytes) from which this header was read--if that happened.<br>
+	 * This is not part of the block header proper. It is a position in a memory buffer of the start of the block
+	 * (physical record). It is kept for convenience. It is up to the reader to set it.
+	 *
+	 * @param bufferStartingPosition the starting position in the buffer from which this header was read--if that
+	 *            happened.
+	 */
+	public void setBufferStartingPosition(int bufferStartingPosition) {
+		this.bufferStartingPosition = bufferStartingPosition;
+	}
+
+	/**
+	 * Determines where the start of the next block (physical record) header in some buffer is located (in bytes).
+     * This assumes the start position has been maintained by the object performing the buffer read.
+	 *
+	 * @return the start of the next block (physical record) header in some buffer is located (in bytes).
+	 */
+	public int nextBufferStartingPosition() {
+        return getBufferEndingPosition();
+	}
+
+	/**
+	 * Determines where the start of the first event (logical record) in this block (physical record) is located
+     * (in bytes). This assumes the start position has been maintained by the object performing the buffer read.
+	 *
+	 * @return where the start of the first event (logical record) in this block (physical record) is located
+     *         (in bytes). Returns 0 if start is 0, signaling that this entire physical record is part of a
+     *         logical record that spans at least three physical records.
+	 */
+	public int firstEventStartingPosition() {
+		// first data word is start of first event
+        return bufferStartingPosition + 4*headerLength;
+	}
+
+	/**
+	 * 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.
+	 *
+	 * @param position the absolute current position is a byte buffer.
+	 * @return the number of bytes remaining in this block (physical record.)
+	 * @throws EvioException
+	 */
+	public int bytesRemaining(int position) throws EvioException {
+		if (position < bufferStartingPosition) {
+			throw new EvioException("Provided position is less than buffer starting position.");
+		}
+
+		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.");
+		}
+
+		return nextBufferStart - position;
+	}
+
+	/**
+	 * Write myself out a byte buffer. This write is relative--i.e., it uses the current position of the buffer.
+	 *
+	 * @param byteBuffer the byteBuffer to write to.
+	 * @return the number of bytes written, which for a BlockHeader is 32.
+	 */
+	@Override
+	public int write(ByteBuffer byteBuffer) {
+		byteBuffer.putInt(size);
+		byteBuffer.putInt(number);
+		byteBuffer.putInt(headerLength); // should always be 8
+		byteBuffer.putInt(eventCount);
+        byteBuffer.putInt(0);            // unused
+		byteBuffer.putInt(getSixthWord());
+		byteBuffer.putInt(0);            // unused
+		byteBuffer.putInt(magicNumber);
+		return 32;
+	}
+}
\ No newline at end of file

jevio-base/src/main/java/org/jlab/coda/jevio
ByteDataTransformer.java 1.2 -> 1.3
diff -N ByteDataTransformer.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ByteDataTransformer.java	28 Feb 2012 19:41:36 -0000	1.3
@@ -0,0 +1,1440 @@
+package org.jlab.coda.jevio;
+
+import java.io.ByteArrayOutputStream;
+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
+ * other types and vice versa as well as other handy methods.
+ * 
+ * @author heddle
+ * @author timmer
+ * @author wolin
+ *
+ */
+public class ByteDataTransformer {
+
+    /**
+     * Converts a byte array into an int array.
+     * Use {@link #toIntArray(byte[], java.nio.ByteOrder)} for better performance.
+     *
+     * @param bytes the byte array.
+     * @param byteOrder the endianness of the data in the byte array,
+     *       {@link ByteOrder#BIG_ENDIAN} or {@link ByteOrder#LITTLE_ENDIAN}.
+     * @return the raw bytes converted into an int array.
+     */
+    public static int[] getAsIntArray(byte bytes[], ByteOrder byteOrder) {
+        ByteBuffer byteBuffer = ByteBuffer.wrap(bytes).order(byteOrder);
+        int intsize = bytes.length / 4;
+        int array[] = new int[intsize];
+        for (int i = 0; i < intsize; i++) {
+            array[i] = byteBuffer.getInt();
+        }
+        return array;
+    }
+
+
+    /**
+     * Converts a byte array into an short array.
+     * Use {@link #toShortArray(byte[], java.nio.ByteOrder)} for better performance.
+     *
+     * @param bytes the byte array.
+     * @param byteOrder the endianness of the data in the byte array,
+     *       {@link ByteOrder#BIG_ENDIAN} or {@link ByteOrder#LITTLE_ENDIAN}.
+     * @return the raw bytes converted into an int array.
+     */
+    public static short[] getAsShortArray(byte bytes[], ByteOrder byteOrder) {
+        ByteBuffer byteBuffer = ByteBuffer.wrap(bytes).order(byteOrder);
+        int shortsize = bytes.length / 2;
+        short array[] = new short[shortsize];
+        for (int i = 0; i < shortsize; i++) {
+            array[i] = byteBuffer.getShort();
+        }
+        return array;
+    }
+
+	/**
+	 * Converts a byte array into an short array while accounting for padding.
+     * Use {@link #toShortArray(byte[], int, java.nio.ByteOrder)} for better performance.
+	 *
+     * @param bytes the byte array.
+     * @param padding number of <b>bytes</b> at the end of the byte array to ignore.
+     *                Valid values are 0 and mulitples of 2 (though only 0 & 2 are used).
+     * @param byteOrder the endianness of the data in the byte array,
+     *       {@link ByteOrder#BIG_ENDIAN} or {@link ByteOrder#LITTLE_ENDIAN}.
+	 * @return the raw bytes converted into an int array.
+	 */
+	public static short[] getAsShortArray(byte bytes[], int padding, ByteOrder byteOrder) {
+		ByteBuffer byteBuffer = ByteBuffer.wrap(bytes).order(byteOrder);
+		int shortsize = bytes.length / 2;
+        if (padding%2 == 0) {
+            shortsize -= padding/2;
+        }
+		short array[] = new short[shortsize];
+		for (int i = 0; i < shortsize; i++) {
+			array[i] = byteBuffer.getShort();
+		}
+		return array;
+	}
+
+	
+	/**
+	 * Converts a byte array into a long array.
+     * Use {@link #toLongArray(byte[], java.nio.ByteOrder)} for better performance.
+	 *
+	 * @param bytes the byte array.
+     * @param byteOrder the endianness of the data in the byte array,
+     *       {@link ByteOrder#BIG_ENDIAN} or {@link ByteOrder#LITTLE_ENDIAN}.
+	 * @return the raw bytes converted into a long array.
+	 */
+	public static long[] getAsLongArray(byte bytes[], ByteOrder byteOrder) {
+		ByteBuffer byteBuffer = ByteBuffer.wrap(bytes).order(byteOrder);
+		int longsize = bytes.length / 8;
+		long array[] = new long[longsize];
+		for (int i = 0; i < longsize; i++) {
+			array[i] = byteBuffer.getLong();
+		}
+		return array;
+	}
+
+	/**
+	 * Converts a byte array into a double array.
+     * Use {@link #toDoubleArray(byte[], java.nio.ByteOrder)} for better performance.
+	 *
+	 * @param bytes the byte array.
+     * @param byteOrder the endianness of the data in the byte array,
+     *       {@link ByteOrder#BIG_ENDIAN} or {@link ByteOrder#LITTLE_ENDIAN}.
+	 * @return the raw bytes converted into a double array.
+	 */
+	public static double[] getAsDoubleArray(byte bytes[], ByteOrder byteOrder) {
+		ByteBuffer byteBuffer = ByteBuffer.wrap(bytes).order(byteOrder);
+		int doublesize = bytes.length / 8;
+		double array[] = new double[doublesize];
+		for (int i = 0; i < doublesize; i++) {
+			array[i] = byteBuffer.getDouble();
+		}
+		return array;
+	}
+	
+	/**
+	 * Converts a byte array into a float array.
+     * Use {@link #toFloatArray(byte[], java.nio.ByteOrder)} for better performance.
+	 *
+	 * @param bytes the byte array.
+     * @param byteOrder the endianness of the data in the byte array,
+     *       {@link ByteOrder#BIG_ENDIAN} or {@link ByteOrder#LITTLE_ENDIAN}.
+	 * @return the raw bytes converted into a float array.
+	 */
+	public static float[] getAsFloatArray(byte bytes[], ByteOrder byteOrder) {
+		ByteBuffer byteBuffer = ByteBuffer.wrap(bytes).order(byteOrder);
+		int floatsize = bytes.length / 4;
+		float array[] = new float[floatsize];
+		for (int i = 0; i < floatsize; i++) {
+			array[i] = byteBuffer.getFloat();
+		}
+		return array;
+	}
+
+
+    /**
+     * Copies a 2-byte short value into a 4-byte int while preserving bit pattern.
+     * In other words, it treats the short as an unsigned value. The resulting int
+     * does NOT undergo sign extension (bits of highest 2 bytes are not all ones),
+     * and will not be a negative number.
+     *
+     * @param shortVal short value considered to be unsigned
+     * @return the equivalent int value
+     */
+    public static final int shortBitsToInt(short shortVal) {
+        return (shortVal & 0x0000ffff);
+    }
+
+
+    /**
+     * Copies a 1-byte byte value into a 4-byte int while preserving bit pattern.
+     * In other words, it treats the byte as an unsigned value. The resulting int
+     * does NOT undergo sign extension (bits of highest 3 bytes are not all ones),
+     * and will not be a negative number.
+     *
+     * @param byteVal byte value considered to be unsigned
+     * @return the equivalent int value
+     */
+    public static final int byteBitsToInt(byte byteVal) {
+        return (byteVal & 0x000000ff);
+    }
+
+
+    // =========================
+    // primitive type --> byte[]
+    // =========================
+
+    /**
+     * Copies an integer value into 4 bytes of a byte array.
+     * @param intVal integer value
+     * @param b byte array
+     * @param off offset into the byte array
+     */
+    public static final void intToBytes(int intVal, byte[] b, int off) {
+        b[off  ] = (byte)(intVal >> 24);
+        b[off+1] = (byte)(intVal >> 16);
+        b[off+2] = (byte)(intVal >>  8);
+        b[off+3] = (byte)(intVal      );
+    }
+
+
+    /**
+     * Copies a short value into 2 bytes of a byte array.
+     * @param val short value
+     * @param b byte array
+     * @param off offset into the byte array
+     */
+    public static final void shortToBytes(short val, byte[] b, int off) {
+        b[off  ] = (byte)(val >>  8);
+        b[off+1] = (byte)(val      );
+    }
+
+
+
+    /**
+     * Turn short into byte array.
+     *
+     * @param data short to convert
+     * @param byteOrder byte order of returned byte array (big endian if null)
+     * @return byte array representing short
+     */
+    public static byte[] toBytes(short data, ByteOrder byteOrder) {
+        if (byteOrder == ByteOrder.BIG_ENDIAN) {
+            return new byte[] {
+                    (byte)(data >>> 8),   // byte cast just truncates data val
+                    (byte)(data),
+            };
+        }
+        else {
+            return new byte[] {
+                    (byte)(data),
+                    (byte)(data >>> 8),
+            };
+        }
+    }
+
+
+    /**
+     * Turn short into byte array.
+     * Avoids creation of new byte array with each call.
+     *
+     * @param data short to convert
+     * @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 dest is null or too small or offset negative
+     */
+    public static void toBytes(short data, ByteOrder byteOrder, byte[] dest, int off)
+            throws EvioException {
+
+        if (dest == null || dest.length < 2+off || off < 0) {
+            throw new EvioException("bad arg(s)");
+        }
+
+        if (byteOrder == null || byteOrder == ByteOrder.BIG_ENDIAN) {
+            dest[off  ] = (byte)(data >>> 8);
+            dest[off+1] = (byte)(data      );
+        }
+        else {
+            dest[off  ] = (byte)(data      );
+            dest[off+1] = (byte)(data >>> 8);
+        }
+    }
+
+
+    /**
+     * Turn short array into byte array.
+     *
+     * @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
+     */
+    public static byte[] toBytes(short[] data, ByteOrder byteOrder) {
+
+        if (data == null) return null;
+        if (byteOrder == null) {
+            byteOrder = ByteOrder.BIG_ENDIAN;
+        }
+
+        byte[] b = new byte[data.length*2];
+
+        ByteBuffer buf = ByteBuffer.wrap(b).order(byteOrder);
+        ShortBuffer ib = buf.asShortBuffer();
+        ib.put(data, 0, data.length);
+
+        return b;
+    }
+
+
+    /**
+      * Turn short array into byte array.
+      * Avoids creation of new byte array with each call.
+      *
+      * @param data short array to convert
+      * @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
+      */
+     public static void toBytes(short[] data, ByteOrder byteOrder, byte[] dest, int off)
+             throws EvioException{
+
+         if (data == null || dest == null || dest.length < 2*data.length+off || off < 0) {
+             throw new EvioException("bad arg(s)");
+         }
+
+         if (byteOrder == null) {
+             byteOrder = ByteOrder.BIG_ENDIAN;
+         }
+
+         ByteBuffer buf = ByteBuffer.wrap(dest).order(byteOrder);
+         buf.position(off);
+         ShortBuffer ib = buf.asShortBuffer();
+         ib.put(data, 0, data.length);
+
+         return;
+     }
+
+
+    // =========================
+
+    /**
+     * Turn int into byte array.
+     *
+     * @param data int to convert
+     * @param byteOrder byte order of returned byte array (big endian if null)
+     * @return byte array representing int
+     */
+    public static byte[] toBytes(int data, ByteOrder byteOrder) {
+        if (byteOrder == null || byteOrder == ByteOrder.BIG_ENDIAN) {
+            return new byte[] {
+                    (byte)(data >> 24),
+                    (byte)(data >> 16),
+                    (byte)(data >>  8),
+                    (byte)(data),
+            };
+        }
+        else {
+            return new byte[] {
+                    (byte)(data),
+                    (byte)(data >>  8),
+                    (byte)(data >> 16),
+                    (byte)(data >> 24),
+            };
+        }
+    }
+
+
+    /**
+     * Turn int into byte array.
+     * Avoids creation of new byte array with each call.
+     *
+     * @param data int to convert
+     * @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 dest is null or too small or offset negative
+     */
+    public static void toBytes(int data, ByteOrder byteOrder, byte[] dest, int off)
+            throws EvioException {
+
+        if (dest == null || dest.length < 4+off || off < 0) {
+            throw new EvioException("bad arg(s)");
+        }
+
+        if (byteOrder == null || byteOrder == ByteOrder.BIG_ENDIAN) {
+            dest[off  ] = (byte)(data >> 24);
+            dest[off+1] = (byte)(data >> 16);
+            dest[off+2] = (byte)(data >>  8);
+            dest[off+3] = (byte)(data      );
+        }
+        else {
+            dest[off  ] = (byte)(data      );
+            dest[off+1] = (byte)(data >>  8);
+            dest[off+2] = (byte)(data >> 16);
+            dest[off+3] = (byte)(data >> 24);
+        }
+    }
+
+
+    /**
+     * Turn int array into byte array.
+     *
+     * @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
+     */
+    public static byte[] toBytes(int[] data, ByteOrder byteOrder) {
+
+        if (data == null) return null;
+        if (byteOrder == null) {
+            byteOrder = ByteOrder.BIG_ENDIAN;
+        }
+
+        byte[] b = new byte[data.length*4];
+
+        ByteBuffer buf = ByteBuffer.wrap(b).order(byteOrder);
+        IntBuffer ib = buf.asIntBuffer();
+        ib.put(data, 0, data.length);
+
+        return b;
+    }
+
+
+    /**
+     * Turn int array into byte array.
+     *
+     * @param data int array to convert
+     * @param offset into int array
+     * @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
+     */
+    public static byte[] toBytes(int[] data, int offset, int length, ByteOrder byteOrder) {
+
+        if (data == null) return null;
+        if (offset < 0 || length < 1) return null;
+        if (byteOrder == null) {
+            byteOrder = ByteOrder.BIG_ENDIAN;
+        }
+
+        byte[] b = new byte[length*4];
+
+        ByteBuffer buf = ByteBuffer.wrap(b).order(byteOrder);
+        IntBuffer ib = buf.asIntBuffer();
+        ib.put(data, offset, length);
+
+        return b;
+    }
+
+
+    /**
+     * Turn int array into byte array.
+     * Avoids creation of new byte array with each call.
+     *
+     * @param data int array to convert
+     * @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
+     */
+    public static void toBytes(int[] data, ByteOrder byteOrder, byte[] dest, int off)
+            throws EvioException{
+
+        if (data == null || dest == null || dest.length < 4*data.length+off || off < 0) {
+            throw new EvioException("bad arg(s)");
+        }
+
+        if (byteOrder == null) {
+            byteOrder = ByteOrder.BIG_ENDIAN;
+        }
+
+        ByteBuffer buf = ByteBuffer.wrap(dest).order(byteOrder);
+        buf.position(off);
+        IntBuffer ib = buf.asIntBuffer();
+        ib.put(data, 0, data.length);
+
+        return;
+    }
+
+
+    /**
+     * Turn int array into byte array.
+     * Uses IO streams to do the work - very inefficient.
+     * Keep around for tutorial purposes.
+     *
+     * @param data int array to convert
+     * @param byteOrder byte order of returned bytes (big endian if null)
+     * @return byte array representing int array
+     */
+    public static byte[] toBytesStream(int[] data, ByteOrder byteOrder) {
+
+        if (data == null) return null;
+
+        ByteArrayOutputStream baos = new ByteArrayOutputStream(4*data.length);
+        DataOutputStream out = new DataOutputStream(baos);
+
+        try {
+            for (int i : data) {
+                if (byteOrder == null || byteOrder == ByteOrder.BIG_ENDIAN) {
+                    out.writeInt(i);
+                }
+                else {
+                    out.writeInt(Integer.reverseBytes(i));
+                }
+            }
+            out.flush();
+        }
+        catch (IOException e) {
+            e.printStackTrace();
+        }
+
+        return baos.toByteArray();
+    }
+
+    // =========================
+
+    /**
+     * Turn long into byte array.
+     *
+     * @param data long to convert
+     * @param byteOrder byte order of returned byte array (big endian if null)
+     * @return byte array representing long
+     */
+    public static byte[] toBytes(long data, ByteOrder byteOrder) {
+
+        if (byteOrder == null || byteOrder == ByteOrder.BIG_ENDIAN) {
+            return new byte[] {
+                    (byte)(data >> 56),
+                    (byte)(data >> 48),
+                    (byte)(data >> 40),
+                    (byte)(data >> 32),
+                    (byte)(data >> 24),
+                    (byte)(data >> 16),
+                    (byte)(data >>  8),
+                    (byte)(data      ),
+            };
+        }
+        else {
+            return new byte[] {
+                    (byte)(data      ),
+                    (byte)(data >>  8),
+                    (byte)(data >> 16),
+                    (byte)(data >> 24),
+                    (byte)(data >> 32),
+                    (byte)(data >> 40),
+                    (byte)(data >> 48),
+                    (byte)(data >> 56),
+            };
+        }
+    }
+
+
+    /**
+     * Turn long into byte array.
+     * Avoids creation of new byte array with each call.
+     *
+     * @param data long to convert
+     * @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 dest is null or too small or offset negative
+     */
+    public static void toBytes(long data, ByteOrder byteOrder, byte[] dest, int off)
+            throws EvioException {
+
+        if (dest == null || dest.length < 8+off || off < 0) {
+            throw new EvioException("bad arg(s)");
+        }
+
+        if (byteOrder == null || byteOrder == ByteOrder.BIG_ENDIAN) {
+            dest[off  ] = (byte)(data >> 56);
+            dest[off+1] = (byte)(data >> 48);
+            dest[off+2] = (byte)(data >> 40);
+            dest[off+3] = (byte)(data >> 32);
+            dest[off+4] = (byte)(data >> 24);
+            dest[off+5] = (byte)(data >> 16);
+            dest[off+6] = (byte)(data >>  8);
+            dest[off+7] = (byte)(data      );
+        }
+        else {
+            dest[off  ] = (byte)(data      );
+            dest[off+1] = (byte)(data >>  8);
+            dest[off+2] = (byte)(data >> 16);
+            dest[off+3] = (byte)(data >> 24);
+            dest[off+4] = (byte)(data >> 32);
+            dest[off+5] = (byte)(data >> 40);
+            dest[off+6] = (byte)(data >> 48);
+            dest[off+7] = (byte)(data >> 56);
+        }
+    }
+
+
+    /**
+     * Turn long array into byte array.
+     *
+     * @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
+     */
+    public static byte[] toBytes(long[] data, ByteOrder byteOrder) {
+
+        if (data == null) return null;
+        if (byteOrder == null) {
+            byteOrder = ByteOrder.BIG_ENDIAN;
+        }
+
+        byte[] b = new byte[data.length*8];
+
+        ByteBuffer buf = ByteBuffer.wrap(b).order(byteOrder);
+        LongBuffer ib = buf.asLongBuffer();
+        ib.put(data, 0, data.length);
+
+        return b;
+    }
+
+
+    /**
+     * Turn long array into byte array.
+     * Avoids creation of new byte array with each call.
+     *
+     * @param data long array to convert
+     * @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
+     */
+    public static void toBytes(long[] data, ByteOrder byteOrder, byte[] dest, int off)
+            throws EvioException{
+
+        if (data == null || dest == null || dest.length < 8*data.length+off || off < 0) {
+            throw new EvioException("bad arg(s)");
+        }
+
+        if (byteOrder == null) {
+            byteOrder = ByteOrder.BIG_ENDIAN;
+        }
+
+        ByteBuffer buf = ByteBuffer.wrap(dest).order(byteOrder);
+        buf.position(off);
+        LongBuffer ib = buf.asLongBuffer();
+        ib.put(data, 0, data.length);
+
+        return;
+    }
+
+
+    // =========================
+
+    /**
+     * Turn float into byte array.
+     *
+     * @param data float to convert
+     * @param byteOrder byte order of returned byte array (big endian if null)
+     * @return byte array representing float
+     */
+    public static byte[] toBytes(float data, ByteOrder byteOrder) {
+        return toBytes(Float.floatToRawIntBits(data), byteOrder);
+    }
+
+    /**
+     * Turn float into byte array.
+     * Avoids creation of new byte array with each call.
+     *
+     * @param data float to convert
+     * @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 dest is null or too small or offset negative
+     */
+    public static void toBytes(float data, ByteOrder byteOrder, byte[] dest, int off)
+            throws EvioException {
+
+        toBytes(Float.floatToRawIntBits(data), byteOrder, dest, off);
+    }
+
+    /**
+     * Turn float array into byte array.
+     *
+     * @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
+     */
+    public static byte[] toBytes(float[] data, ByteOrder byteOrder) {
+
+        if (data == null) return null;
+        if (byteOrder == null) {
+            byteOrder = ByteOrder.BIG_ENDIAN;
+        }
+
+        byte[] b = new byte[data.length*4];
+
+        ByteBuffer buf = ByteBuffer.wrap(b).order(byteOrder);
+        FloatBuffer ib = buf.asFloatBuffer();
+        ib.put(data, 0, data.length);
+
+        return b;
+    }
+
+    /**
+     * Turn float array into byte array.
+     * Avoids creation of new byte array with each call.
+     *
+     * @param data float array to convert
+     * @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
+     */
+    public static void toBytes(float[] data, ByteOrder byteOrder, byte[] dest, int off)
+            throws EvioException{
+
+        if (data == null || dest == null || dest.length < 4*data.length+off || off < 0) {
+            throw new EvioException("bad arg(s)");
+        }
+
+        if (byteOrder == null) {
+            byteOrder = ByteOrder.BIG_ENDIAN;
+        }
+
+        ByteBuffer buf = ByteBuffer.wrap(dest).order(byteOrder);
+        buf.position(off);
+        FloatBuffer ib = buf.asFloatBuffer();
+        ib.put(data, 0, data.length);
+
+        return;
+    }
+
+    // =========================
+
+    /**
+     * Turn double into byte array.
+     *
+     * @param data double to convert
+     * @param byteOrder byte order of returned byte array (big endian if null)
+     * @return byte array representing double
+     */
+    public static byte[] toBytes(double data, ByteOrder byteOrder) {
+        return toBytes(Double.doubleToRawLongBits(data), byteOrder);
+    }
+
+    /**
+     * Turn double into byte array.
+     * Avoids creation of new byte array with each call.
+     *
+     * @param data double to convert
+     * @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 dest is null or too small or offset negative
+     */
+    public static void toBytes(double data, ByteOrder byteOrder, byte[] dest, int off)
+            throws EvioException {
+
+        toBytes(Double.doubleToRawLongBits(data), byteOrder, dest, off);
+    }
+
+    /**
+     * Turn double array into byte array.
+     *
+     * @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
+     */
+    public static byte[] toBytes(double[] data, ByteOrder byteOrder) {
+
+        if (data == null) return null;
+        if (byteOrder == null) {
+            byteOrder = ByteOrder.BIG_ENDIAN;
+        }
+
+        byte[] b = new byte[data.length*8];
+
+        ByteBuffer buf = ByteBuffer.wrap(b).order(byteOrder);
+        DoubleBuffer ib = buf.asDoubleBuffer();
+        ib.put(data, 0, data.length);
+
+        return b;
+    }
+
+    /**
+     * Turn double array into byte array.
+     * Avoids creation of new byte array with each call.
+     *
+     * @param data double array to convert
+     * @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
+     */
+    public static void toBytes(double[] data, ByteOrder byteOrder, byte[] dest, int off)
+            throws EvioException{
+
+        if (data == null || dest == null || dest.length < 8*data.length+off || off < 0) {
+            throw new EvioException("bad arg(s)");
+        }
+
+        if (byteOrder == null) {
+            byteOrder = ByteOrder.BIG_ENDIAN;
+        }
+
+        ByteBuffer buf = ByteBuffer.wrap(dest).order(byteOrder);
+        buf.position(off);
+        DoubleBuffer ib = buf.asDoubleBuffer();
+        ib.put(data, 0, data.length);
+
+        return;
+    }
+
+    /**
+     * Turn double array into byte array.
+     * Slower than {@link #toBytes(double[], ByteOrder)}.
+     * Keep this around for completeness purposes - a record
+     * of what was already tried and found wanting. Tried this
+     * for all data types.
+     *
+     * @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
+     */
+    public static byte[] toBytes2(double[] data, ByteOrder byteOrder) {
+
+        if (data == null) return null;
+
+        byte[] stor = new byte[8];
+        byte[] byts = new byte[data.length*8];
+
+        try {
+            for (int i = 0; i < data.length; i++) {
+                toBytes(data[i], byteOrder, stor, 0);
+                System.arraycopy(stor, 0, byts, i*8, 8);
+            }
+        }
+        catch (EvioException e) {/* never happen */}
+
+        return byts;
+    }
+
+    /**
+     * Turn double array into byte array.
+     * Avoids creation of new byte array with each call.
+     * Slower than {@link #toBytes(double[], ByteOrder, byte[], int)}.
+     * Keep this around for completeness purposes - a record
+     * of what was already tried and found wanting. Tried this
+     * for all data types.
+     *
+     * @param data double array to convert
+     * @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
+     */
+    public static void toBytes2(double[] data, ByteOrder byteOrder, byte[] dest, int off)
+            throws EvioException{
+
+        if (data == null || dest == null || dest.length < 8*data.length+off || off < 0) {
+            throw new EvioException("bad arg(s)");
+        }
+
+        byte[] stor = new byte[8];
+
+        try {
+            for (int i = 0; i < data.length; i++) {
+                toBytes(data[i], byteOrder, stor, 0);
+                System.arraycopy(stor, 0, dest, off + i*8, 8);
+            }
+        }
+        catch (EvioException e) {/* never happen */}
+
+        return;
+    }
+
+    // =========================
+    // byte[] --> primitive type
+    // =========================
+
+    /**
+     * Turn section of byte array into a short.
+     *
+     * @param data byte array to convert
+     * @param byteOrder byte order of supplied bytes (big endian if null)
+     * @param off offset into data array
+     * @return short converted from byte array
+     * @throws EvioException if data is null or wrong size, or off is negative
+     */
+    public static short toShort(byte[] data, ByteOrder byteOrder, int off) throws EvioException {
+        if (data == null || data.length < 2+off || off < 0) {
+            throw new EvioException("bad data arg");
+        }
+
+        if (byteOrder == null || byteOrder == ByteOrder.BIG_ENDIAN) {
+            return (short)(
+                (0xff & data[  off]) << 8 |
+                (0xff & data[1+off])
+            );
+        }
+        else {
+            return (short)(
+                (0xff & data[  off]) |
+                (0xff & data[1+off] << 8)
+            );
+        }
+    }
+
+    /**
+     * Turn 2 bytes into a short.
+     *
+     * @param b1 1st byte
+     * @param b2 2nd byte
+     * @param byteOrder if big endian, 1st byte is most significant (big endian if null)
+     * @return short converted from byte array
+     */
+    public static short toShort(byte b1, byte b2, ByteOrder byteOrder) {
+
+        if (byteOrder == null || byteOrder == ByteOrder.BIG_ENDIAN) {
+            return (short)(
+                (0xff & b1) << 8 |
+                (0xff & b2)
+            );
+        }
+        else {
+            return (short)(
+                (0xff & b1) |
+                (0xff & b2 << 8)
+            );
+        }
+    }
+
+    /**
+     * Turn byte array into a short array.
+     *
+     * @param data byte array to convert
+     * @param byteOrder byte order of supplied bytes (big endian if null)
+     * @return short array converted from byte array
+     * @throws EvioException if data is null or odd size
+     */
+    public static short[] toShortArray(byte[] data, ByteOrder byteOrder) throws EvioException {
+        if (data == null || data.length % 2 != 0) {
+            throw new EvioException("bad data arg");
+        }
+
+        short[] sa = new short[data.length / 2];
+        for (int i=0; i < sa.length; i++) {
+            sa[i] = toShort(data[(i*2)],
+                            data[(i*2)+1],
+                            byteOrder);
+        }
+        return sa;
+    }
+
+    /**
+     * Turn byte array into a short array while taking padding into account.
+     *
+     * @param data byte array to convert
+     * @param padding number of <b>bytes</b> at the end of the byte array to ignore.
+     *                Valid values are 0 and 2
+     * @param byteOrder byte order of supplied bytes (big endian if null)
+     * @return short array converted from byte array
+     * @throws EvioException if data is null or odd size; padding not 0 or 2
+     */
+    public static short[] toShortArray(byte[] data, int padding, ByteOrder byteOrder) throws EvioException {
+        if (data == null || data.length % 2 != 0 || (padding != 0 && padding != 2)) {
+            throw new EvioException("bad data arg");
+        }
+
+        short[] sa = new short[(data.length - padding)/2];
+        for (int i=0; i < sa.length; i++) {
+            sa[i] = toShort(data[(i*2)],
+                            data[(i*2)+1],
+                            byteOrder);
+        }
+        return sa;
+    }
+
+    /**
+     * Turn byte array into a short array while taking padding into account.
+     *
+     * @param data byte array to convert
+     * @param padding number of <b>bytes</b> at the end of the byte array to ignore.
+     *                Valid values are 0 and 2
+     * @param byteOrder byte order of supplied bytes (big endian if null)
+     * @param dest array in which to write converted bytes
+     * @param off offset into dest array
+     * @throws EvioException if data is null or wrong size; dest is null, wrong size, or off < 0
+     */
+    public static void toShortArray(byte[] data, int padding, ByteOrder byteOrder, short[] dest, int off)
+            throws EvioException {
+
+        if (data == null || data.length % 2 != 0 || (padding != 0 && padding != 2) ||
+            dest == null || 2*dest.length < data.length-padding+off || off < 0) {
+            throw new EvioException("bad data arg");
+        }
+
+        int len = data.length - padding;
+System.out.println("toShortArray: padding = " + padding + ", data len = " + data.length);
+        for (int i = 0; i < data.length - padding - 1; i+=2) {
+            dest[i/2+off] = toShort(data[i],
+                                    data[i+1],
+                                    byteOrder);
+        }
+    }
+
+    /**
+     * Turn byte array into an short array.
+     *
+     * @param data byte array to convert
+     * @param byteOrder byte order of supplied bytes (big endian if null)
+     * @param dest array in which to write converted bytes
+     * @param off offset into dest array
+     * @throws EvioException if data is null or wrong size; dest is null, wrong size, or off < 0
+     */
+    public static void toShortArray(byte[] data, ByteOrder byteOrder, short[] dest, int off)
+            throws EvioException {
+
+        if (data == null || data.length % 2 != 0 ||
+            dest == null || 2*dest.length < data.length+off || off < 0) {
+            throw new EvioException("bad data arg");
+        }
+
+        for (int i = 0; i < data.length-1; i+=2) {
+            dest[i/2+off] = toShort(data[i],
+                                    data[i + 1],
+                                    byteOrder);
+        }
+    }
+
+	    // =========================
+
+    /**
[truncated at 1000 lines; 444 more skipped]

jevio-base/src/main/java/org/jlab/coda/jevio
CompositeData.java added at 1.1
diff -N CompositeData.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ CompositeData.java	28 Feb 2012 19:41:36 -0000	1.1
@@ -0,0 +1,1795 @@
+/**
+ * ################################
+ * COMPOSITE DATA:
+ * ################################
+ * This is a new type of data (value = 0xf) which originated with Hall B.
+ * It is a composite type and allows for possible expansion in the future
+ * if there is a demand. Basically it allows the user to specify a custom
+ * format by means of a string - stored in a tagsegment. The data in that
+ * format follows in a bank. The routine to swap this data must be provided
+ * by the definer of the composite type - in this case Hall B. The swapping
+ * function is plugged into this evio library's swapping routine.
+ *
+ * Here's what the data looks like.
+ *
+ * MSB(31)                          LSB(0)
+ * <---  32 bits ------------------------>
+ * _______________________________________
+ * |  tag    | type |    length          | --> tagsegment header
+ * |_________|______|____________________|
+ * |        Data Format String           |
+ * |                                     |
+ * |_____________________________________|
+ * |              length                 | \
+ * |_____________________________________|  \  bank header
+ * |       tag      |  type   |   num    |  /
+ * |________________|_________|__________| /
+ * |               Data                  |
+ * |                                     |
+ * |_____________________________________|
+ *
+ * The beginning tagsegment is a normal evio tagsegment containing a string
+ * (type = 0x3). Currently its type and tag are not used - at least not for
+ * data formatting.
+ * The bank is a normal evio bank header with data following.
+ * The format string is used to read/write this data so that takes care of any
+ * padding that may exist. As with the tagsegment, the tags and type are ignored.
+ */
+package org.jlab.coda.jevio;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This is the class defining the composite data type.
+ * It is a mixture of header and raw data.
+ *
+ * @author timmer
+ * 
+ */
+public final class CompositeData {
+
+    /** String containing data format. */
+    private String format;
+
+    /** List of ints obtained from transforming format string. */
+    private List<Integer> formatInts;
+
+    /** List of extracted data items from raw bytes. */
+    private List<Object> items;
+
+    /** List of the types of the extracted data items. */
+    private List<DataType> types;
+
+    /** Tagsegment header of tagsegment containing format string. */
+    private TagSegmentHeader tsHeader;
+
+    /** Bank header of bank containing data. */
+    private BankHeader bHeader;
+
+    /** The entire raw data of the composite item - both tagsegment and data bank. */
+    private byte rawBytes[];
+
+	/** Buffer containing only the data of the composite item (no headers). */
+	private ByteBuffer dataBuffer;
+
+    /** Length of only data in bytes. */
+    private int dataBytes;
+
+    /** Offset in rawBytes to place of data. */
+    private int dataOffset;
+
+    /** Byte order of raw bytes. */
+    private ByteOrder byteOrder;
+
+    /** Index used in getting data items from the {@link #items} list. */
+    private int getIndex;
+
+
+
+    /** Zero arg constructor used for cloning. */
+    private CompositeData() {}
+
+	/**
+	 * Constructor.
+     *
+     * @param rawBytes  raw data defining this composite type item
+     * @param byteOrder byte order of rawBytes
+	 */
+	public CompositeData(byte[] rawBytes, ByteOrder byteOrder)
+            throws EvioException {
+
+        boolean debug = true;
+
+        if (rawBytes == null) {
+            throw new EvioException("null argument(s)");
+        }
+
+        if (debug) System.out.println("CompositeData constr: incoming byte order = " + byteOrder);
+
+        if (byteOrder == null) {
+            byteOrder = ByteOrder.BIG_ENDIAN;
+        }
+
+        if (debug) System.out.println("Analyzing composite data:");
+
+        this.rawBytes  = rawBytes;
+        this.byteOrder = byteOrder;
+
+        // First read the tag segment header
+        tsHeader = EventParser.createTagSegmentHeader(rawBytes, 0, byteOrder);
+
+        if (debug) {
+            System.out.println("    tagseg: type = " + Integer.toHexString(tsHeader.getDataTypeValue()) +
+                                ", tag = " + tsHeader.getTag() + ", len = " + tsHeader.getLength());
+        }
+
+        // Hop over tagseg header
+        dataOffset = tsHeader.getHeaderLength();
+
+        // Read the format string it contains
+        String[] strs = BaseStructure.unpackRawBytesToStrings(rawBytes, 4*dataOffset);
+
+        if (strs.length < 1) {
+           throw new EvioException("bad format string data");
+        }
+        format = strs[0];
+
+        if (debug) {
+            System.out.println("    format: " + format);
+        }
+
+        formatInts = compositeFormatToInt(format);
+        if (formatInts.size() < 1) {
+           throw new EvioException("bad format string data");
+        }
+
+        // Hop over tagseg data
+        dataOffset += tsHeader.getLength() - (tsHeader.getHeaderLength() - 1);
+
+        // Read the data bank header
+        bHeader = EventParser.createBankHeader(rawBytes, 4*dataOffset, byteOrder);
+
+        // Hop over bank header
+        dataOffset += bHeader.getHeaderLength();
+
+        // How many bytes do we skip over at the end?
+        int padding = bHeader.getPadding();
+
+        // How much data do we have?
+        dataBytes = 4*(bHeader.getLength() - (bHeader.getHeaderLength() - 1)) - padding;
+        if (dataBytes < 2) {
+           throw new EvioException("no composite data");
+        }
+
+        if (debug) {
+            System.out.println("    bank: type = " + Integer.toHexString(bHeader.getDataTypeValue()) +
+                                ", tag = " + bHeader.getTag() + ", num = " + bHeader.getNumber());
+            System.out.println("    bank: len (words) = " + bHeader.getLength() +
+                               ", data len - padding = " + dataBytes);
+        }
+
+        // Put data into ByteBuffer object
+        dataBuffer = ByteBuffer.wrap(rawBytes, 4*dataOffset, dataBytes).slice();
+
+        // Turn dataBuffer into list of items and their types
+        process();
+	}
+
+
+    /**
+     * Method to clone a CompositeData object.
+     * Deep cloned so no connection exists between
+     * clone and object cloned.
+     *
+     * @return cloned CompositeData object.
+     */
+    public Object clone() {
+
+        CompositeData cd = new CompositeData();
+
+        cd.getIndex   = getIndex;
+        cd.byteOrder  = byteOrder;
+        cd.dataBytes  = dataBytes;
+        cd.dataOffset = dataOffset;
+        cd.rawBytes   = rawBytes.clone();
+
+        // copy tagSegment header
+        cd.tsHeader = new TagSegmentHeader(tsHeader.getTag(),
+                                           tsHeader.getDataType());
+
+        // copy bank header
+        cd.bHeader = new BankHeader(bHeader.getTag(),
+                                    bHeader.getDataType(),
+                                    bHeader.getNumber());
+
+        // copy ByteBuffer
+        cd.dataBuffer = ByteBuffer.wrap(rawBytes, 4*dataOffset, 4*bHeader.getLength()).slice();
+
+        // copy format ints
+        cd.formatInts = new ArrayList<Integer>(formatInts.size());
+        cd.formatInts.addAll(formatInts);
+
+        // copy type items
+        cd.types = new ArrayList<DataType>(types.size());
+        cd.types.addAll(types);
+
+        //-----------------
+        // copy data items
+        //-----------------
+
+        // create new list of correct length
+        int listLength = items.size();
+        cd.items = new ArrayList<Object>(listLength);
+
+        // copy each item, cloning those that are mutable
+        for (int i=0; i < listLength; i++) {
+            switch (types.get(i)) {
+                // this is the only mutable type and so
+                // it needs to be cloned specifically
+                case CHARSTAR8:
+                    String[] strs = (String[]) items.get(i);
+                    cd.items.add(strs.clone());
+                    break;
+                default:
+                    cd.items.add(items.get(i));
+            }
+        }
+
+        return cd;
+    }
+
+    /**
+     * This method gets a list of all the data items inside the composite.
+     * @return list of all the data items inside the composite.
+     */
+    public List<Object> getItems() {
+        return items;
+    }
+
+    /**
+     * This method gets a list of all the types of the data items inside the composite.
+     * @return list of all the types of the data items inside the composite.
+     */
+    public List<DataType> getTypes() {
+        return types;
+    }
+
+    /**
+     * This methods returns the index of the data item to be returned
+     * on the next call to one of the get&lt;Type&gt;() methods
+     * (e.g. {@link #getInt()}.
+     * @return returns the index of the data item to be returned
+     */
+    public int index() {
+        return getIndex;
+    }
+
+    /**
+     * This methods sets the index of the data item to be returned
+     * on the next call to one of the get&lt;Type&gt;() methods
+     * (e.g. {@link #getInt()}.
+     * @param index the index of the next data item to be returned
+     */
+    public void index(int index) {
+        this.getIndex = index;
+    }
+
+    /**
+     * This method gets the next data item as an Integer object.
+     * @return Integer object, if that is the correct type of the next data item.
+     * @return null if no more data items or data item is not a 32 bit signed or
+     *         unsigned integer
+     */
+    public Integer getInt() {
+        // Past end of data, return null
+        if (getIndex > types.size()) {
+            return null;
+        }
+
+        // Check for type mismatch to avoid ClassCastException
+        DataType type = types.get(getIndex);
+        if (type != DataType.INT32 && type != DataType.UINT32) {
+            return null;
+        }
+
+        return (Integer) (items.get(getIndex++));
+    }
+
+    /**
+     * This method gets the next data item (which is of Hollerit type) as an Integer object.
+     * @return Integer object, if Hollerit is the type of the next data item.
+     * @return null if no more data items or data item is not a 32 bit signed or
+     *         unsigned integer
+     */
+    public Integer getHollerit() {
+        if (getIndex > types.size()) return null;
+        DataType type = types.get(getIndex);
+        if (type != DataType.HOLLERIT) return null;
+        return (Integer) (items.get(getIndex++));
+    }
+
+    /**
+     * This method gets the next data item as a Byte object.
+     * @return Byte object, if that is the correct type of the next data item.
+     * @return null if no more data items or data item is not a 8 bit signed or
+     *         unsigned integer
+     */
+    public Byte getByte() {
+        if (getIndex > types.size()) return null;
+        DataType type = types.get(getIndex);
+        if (type != DataType.CHAR8 && type != DataType.UCHAR8) return null;
+        return (Byte) (items.get(getIndex++));
+    }
+
+    /**
+     * This method gets the next data item as a Short object.
+     * @return Short object, if that is the correct type of the next data item.
+     * @return null if no more data items or data item is not a 16 bit signed or
+     *         unsigned integer
+     */
+    public Short getShort() {
+        if (getIndex > types.size()) return null;
+        DataType type = types.get(getIndex);
+        if (type != DataType.SHORT16 && type != DataType.USHORT16) return null;
+        return (Short) (items.get(getIndex++));
+    }
+
+    /**
+     * This method gets the next data item as a Long object.
+     * @return Long object, if that is the correct type of the next data item.
+     * @return null if no more data items or data item is not a 64 bit signed or
+     *         unsigned integer
+     */
+    public Long getLong() {
+        if (getIndex > types.size()) return null;
+        DataType type = types.get(getIndex);
+        if (type != DataType.LONG64 && type != DataType.ULONG64) return null;
+        return (Long) (items.get(getIndex++));
+    }
+
+    /**
+     * This method gets the next data item as a Float object.
+     * @return Float object, if that is the correct type of the next data item.
+     * @return null if no more data items or data item is not a 32 bit float.
+     */
+    public Float getFloat() {
+        if (getIndex > types.size()) return null;
+        DataType type = types.get(getIndex);
+        if (type != DataType.FLOAT32) return null;
+        return (Float) (items.get(getIndex++));
+    }
+
+    /**
+     * This method gets the next data item as a Double object.
+     * @return Double object, if that is the correct type of the next data item.
+     * @return null if no more data items or data item is not a 32 bit double.
+     */
+    public Double getDouble() {
+        if (getIndex > types.size()) return null;
+        DataType type = types.get(getIndex);
+        if (type != DataType.DOUBLE64) return null;
+        return (Double) (items.get(getIndex++));
+    }
+
+    /**
+     * This method gets the next data item as a String array.
+     * @return String array, if that is the correct type of the next data item.
+     * @return null if no more data items or data item is not a string or string array.
+     */
+    public String[] getStrings() {
+        if (getIndex > types.size()) return null;
+        DataType type = types.get(getIndex);
+        if (type != DataType.CHARSTAR8)  return null;
+        return (String[]) (items.get(getIndex++));
+    }
+
+
+    static int max(int a, int b) {
+        return (a > b ? a : b);
+    }
+
+    static int min(int a, int b) {
+        return (a < b ? a : b);
+    }
+
+
+    /**
+     *  This method transforms a composite, format-containing
+     *  ASCII string to an int array. It is to be used in
+     *  conjunction with {@link #swapData(ByteBuffer, ByteBuffer, int, List)}
+     *  to swap the endianness of composite data.
+     *  It's translated from the eviofmt C function.
+     *
+     *   format code bits <- format in ascii form
+     *     [7:4] [3:0]
+     *       #     0           #'('
+     *       0    15           #'(' same as above, but have to take # from the data (32-bit)
+     *       0     0            ')'
+     *       #     1           #'i'   unsigned int
+     *       #     2           #'F'   floating point
+     *       #     3           #'a'   8-bit char (C++)
+     *       #     4           #'S'   short
+     *       #     5           #'s'   unsigned short
+     *       #     6           #'C'   char
+     *       #     7           #'c'   unsigned char
+     *       #     8           #'D'   double (64-bit float)
+     *       #     9           #'L'   long long (64-bit int)
+     *       #    10           #'l'   unsigned long long (64-bit int)
+     *       #    11           #'I'   int
+     *       #    12           #'A'   hollerit (4-byte char with int ending)
+     *
+     *   NOTES:
+     *    1. If format ends but end of data did not reach, format in last parenthesis
+     *       will be repeated until all data processed; if there are no parenthesis
+     *       in format, data processing will be started from the beginnig of the format
+     *       (FORTRAN agreement)
+     *    2. The number of repeats '#' must be the number between 2 and 15; if the number
+     *       of repeats is symbol 'N' instead of the number, it will be taken from data
+     *       assuming 'int' format
+     *
+     *  @param  fmt composite data format string
+     *  @return List of ints resulting from transformation of "fmt" string
+     *  @throws EvioException if improper format string
+     */
+    public static List<Integer> compositeFormatToInt(String fmt) throws EvioException {
+
+        char ch;
+        int l, kf, lev, nr, nn;
+        boolean debug = false;
+
+        ArrayList<Integer> ifmt = new ArrayList<Integer>(2*fmt.length());
+
+        nr  = 0;
+        nn  = 1;
+        lev = 0;
+
+        if (debug) {
+            System.out.println("\nfmt >" + fmt + "<");
+        }
+
+        // loop over format string character-by-character
+        for (l=0; l < fmt.length(); l++) {
+            ch = fmt.charAt(l);
+
+            if (ch == ' ') continue;
+
+            if (debug) {
+                System.out.println(ch);
+            }
+
+            // if char = digit, following before comma will be repeated 'number' times
+            if (Character.isDigit(ch)) {
+                if (nr < 0) throw new EvioException("no negative repeats");
+                nr = 10*max(0,nr) + Character.digit(ch,10);
+                if (nr > 15) throw new EvioException("no more than 15 repeats allowed");
+                if (debug) {
+                    System.out.println("the number of repeats nr = " + nr);
+                }
+            }
+            // a left parenthesis -> 16*nr + 0
+            else if (ch == '(')  {
+                if (nr < 0) throw new EvioException("no negative repeats");
+                lev++;
+
+                if (debug) {
+                    System.out.println(String.format("111: nn=%d nr=%d\n",nn,nr));
+                }
+
+                // special case: if #repeats is in data, use code '15'
+                if (nn == 0) {
+                    ifmt.add(15);
+                }
+                else {
+                    ifmt.add(16*max(nn,nr));
+                }
+
+                nn = 1;
+                nr = 0;
+
+                if (debug) {
+                    int ii = ifmt.size() - 1;
+                    String s = String.format("  [%3d] S%3d (16 * %2d + %2d), nr=%d\n",
+                                             ii,ifmt.get(ii),ifmt.get(ii)/16,
+                                             ifmt.get(ii)-(ifmt.get(ii)/16)*16,nr);
+                    System.out.println(s);
+                }
+
+            }
+            // a right parenthesis -> 16*0 + 0
+            else if (ch == ')') {
+                if (nr >= 0) throw new EvioException("cannot repeat right parenthesis");
+                lev--;
+                ifmt.add(0);
+                nr = -1;
+
+                if (debug) {
+                    int ii = ifmt.size() - 1;
+                    String s = String.format("  [%3d] S%3d (16 * %2d + %2d), nr=%d\n",
+                                             ii,ifmt.get(ii),ifmt.get(ii)/16,
+                                             ifmt.get(ii)-(ifmt.get(ii)/16)*16,nr);
+                    System.out.println(s);
+                }
+
+            }
+            // a comma, reset nr
+            else if (ch == ',') {
+                if (nr >= 0) throw new EvioException("cannot repeat comma");
+                nr = 0;
+                if (debug)
+                    System.out.println(String.format("komma, nr=%d",nr));
+
+            }
+            // variable length format
+            else if (ch == 'N') {
+                nn = 0;
+                if (debug)
+                    System.out.println("nn");
+
+            }
+            // actual format
+            else {
+                if      (ch == 'i') kf = 1;  // 32
+                else if (ch == 'F') kf = 2;  // 32
+                else if (ch == 'a') kf = 3;  //  8
+                else if (ch == 'S') kf = 4;  // 16
+                else if (ch == 's') kf = 5;  // 16
+                else if (ch == 'C') kf = 6;  //  8
+                else if (ch == 'c') kf = 7;  //  8
+                else if (ch == 'D') kf = 8;  // 64
+                else if (ch == 'L') kf = 9;  // 64
+                else if (ch == 'l') kf = 10; // 64
+                else if (ch == 'I') kf = 11; // 32
+                else if (ch == 'A') kf = 12; // 32
+                else                kf = 0;
+
+                if (kf != 0) {
+                    if (nr < 0) throw new EvioException("no negative repeats");
+
+                    if (debug) {
+                        System.out.println(String.format("222: nn=%d nr=%d\n",nn,nr));
+                    }
+
+                    ifmt.add(16*max(nn,nr) + kf);
+                    nn=1;
+
+                    if (debug) {
+                        int ii = ifmt.size() - 1;
+                        String s = String.format("  [%3d] S%3d (16 * %2d + %2d), nr=%d\n",
+                                                 ii,ifmt.get(ii),ifmt.get(ii)/16,
+                                                 ifmt.get(ii)-(ifmt.get(ii)/16)*16,nr);
+                        System.out.println(s);
+                    }
+                }
+                else {
+                    throw new EvioException("illegal character");
+                }
+                nr = -1;
+            }
+        }
+
+        if (lev != 0) {
+            throw new EvioException("mismatched number of right/left parentheses");
+        }
+
+        if (debug) System.out.println();
+
+        return (ifmt);
+    }
+
+
+    /**
+     * 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.
+     *
+     * @param src      source data array (of 32 bit words)
+     * @param srcOff   # of bytes offset into source data array
+     * @param dest     destination data array (of 32 bit words)
+     * @param destOff  # of bytes offset into destination data array
+     * @param length   length of data array in 32 bit words
+     * @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
+     */
+    public static void swapAll (byte[] src, int srcOff, byte[] dest, int destOff,
+                                int length, ByteOrder srcOrder) throws EvioException {
+
+        if (src == null) {
+            throw new EvioException("null argument(s)");
+        }
+
+        boolean inPlace = false;
+        if (dest == null) {
+            dest = src;
+            inPlace = true;
+        }
+
+        if (srcOff < 0 || destOff < 0 || length < 0) {
+            throw new EvioException("offsets or length must be >= 0");
+        }
+
+        if ((dest == src) && (srcOff != destOff)) {
+            throw new EvioException("if src = dest, offsets cannot differ");
+        }
+
+        // Byte order of swapped data
+        ByteOrder destOrder = (srcOrder == ByteOrder.BIG_ENDIAN) ?
+                               ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN;
+
+        // Move to beginning of tagseg header
+        int srcDataOffset  = srcOff;
+        int destDataOffset = destOff;
+
+        ByteBuffer  srcBuffer = ByteBuffer.wrap( src,  srcDataOffset, 4*length);
+        ByteBuffer destBuffer = ByteBuffer.wrap(dest, destDataOffset, 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);
+
+System.out.println("theaderLen = "+headerLen+ ", theader.getLn() = " +
+                                   tsHeader.getLength()+ ", tdataLen = " + dataLength);
+
+        // Oops, no format data
+        if (dataLength < 1) {
+           throw new EvioException("no format data");
+        }
+
+        // Got all we needed from the tagseg header, now swap as it's written out.
+        tsHeader.write(destBuffer);
+
+        // Move to beginning of string data
+        srcDataOffset  += 4*headerLen;
+        destDataOffset += 4*headerLen;
+
+        // Read the format string it contains
+        String[] strs = BaseStructure.unpackRawBytesToStrings(src, srcDataOffset);
+
+        if (strs.length < 1) {
+           throw new EvioException("bad format string data");
+        }
+        String format = strs[0];
+
+        // Transform string format into int array format
+        List<Integer> formatInts = compositeFormatToInt(format);
+        if (formatInts.size() < 1) {
+           throw new EvioException("bad format string data");
+        }
+
+        // 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);
+        }
+
+        // Move to beginning of bank header
+        srcDataOffset  += 4*dataLength;
+        destDataOffset += 4*dataLength;
+
+        // 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);
+
+        // Swap data
+        swapData(srcBuffer, destBuffer, dataLength, formatInts);
+    }
+
+
+    /**
+     * This method converts (swaps) an array of EVIO composite type data
+     * between IEEE (big endian) and DECS (little endian). This
+     * data does <b>NOT</b> include the composite type's beginning tagsegment and
+     * the format string it contains. It also does <b>NOT</b> include the data's
+     * bank header words.
+     *
+     * @param src      source data array (of 32 bit words)
+     * @param srcOff   offset into source data array
+     * @param dest     destination data array (of 32 bit words)
+     * @param destOff  offset into destination data array
+     * @param nBytes   length of data array in bytes
+     * @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
+     */
+    public static void swapData(byte[] src, int srcOff, byte[] dest, int destOff,
+                                int nBytes, List<Integer> ifmt, ByteOrder srcOrder)
+                        throws EvioException {
+
+        if (src == null) throw new EvioException("src arg cannot be null");
+
+        ByteBuffer srcBuf = ByteBuffer.wrap(src, srcOff, nBytes);
+        srcBuf.order(srcOrder);
+
+        ByteBuffer destBuf;
+
+        if (src == dest) {
+            if (srcOff != destOff) {
+                throw new EvioException("if src = dest, offsets cannot differ");
+            }
+            dest = null;
+        }
+
+        if (dest == null) {
+            destBuf = null;
+        }
+        else {
+            destBuf = ByteBuffer.wrap(dest, destOff, nBytes);
+        }
+
+        swapData(srcBuf, destBuf, nBytes, ifmt);
+    }
+
+
+    /**
+     * This method converts (swaps) EVIO composite type data
+     * between IEEE (big endian) and DECS (little endian). This
+     * data does <b>NOT</b> include the composite type's beginning tagsegment and
+     * the format string it contains. It also does <b>NOT</b> include the data's
+     * bank header words. Caller must be sure the endian value of the srcBuf
+     * is set properly before the call.<p>
+     * <b>MAKE SURE destBuf IS SET TO THE OPPOSITE ENDIANNESS AS srcBuf OR
+     * NO SWAPPING WILL TAKE PLACE!</b> This can be done by calling
+     * {@link ByteBuffer#order(java.nio.ByteOrder)}.
+     *
+     * @param srcBuf   source data buffer
+     * @param destBuf  destination data buffer; if null, use srcBuf as destination
+     * @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
+     */
+    public static void swapData(ByteBuffer srcBuf, ByteBuffer destBuf,
+                                int nBytes, List<Integer> ifmt)
+                        throws EvioException {
+
+        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 "("
+          int nrepeat; // how many times format in parenthesis must be repeated
+          int irepeat; // right parenthesis ")" counter, or how many times format
+                       // in parenthesis already repeated
+        };
+
+        // check args
+        if (nBytes <= 0 || nfmt <= 0) throw new EvioException("bad argument value(s)");
+
+        if (destBuf == null) destBuf = srcBuf;
+        boolean inPlace = (srcBuf == destBuf);
+
+        LV[] lv = new LV[10];
+        for (int i=0; i < lv.length; i++) {
+            lv[i] = new LV();
+        }
+
+        imt   = 0;  // ifmt[] index
+        ncnf  = 0;  // how many times must repeat a format
+        lev   = 0;  // parenthesis level
+        iterm = 0;
+
+        // Byte order stuff
+        boolean fromLittleEndian = true;
+        if (srcBuf.order() == ByteOrder.BIG_ENDIAN) {
+            fromLittleEndian = false;
+        }
+
+        // start of data, index into src data array
+        int srcIndex = srcBuf.position();
+        // just past end of src data
+        int srcEndIndex = srcIndex + nBytes;
+
+        // place to put swapped data, index into dest data array
+        int destIndex = destBuf.position();
+
+        // check to see if buffers are too small to handle this operation
+        if (srcBuf.capacity() < nBytes || destBuf.capacity() < nBytes) {
+            throw new EvioException("buffer(s) is(are) too small to handle swap");
+        }
+
+        // Sergey's original comments:
+        //
+        //   Converts the data of array (iarr[i], i=0...nwrd-1)
+        //   using the format code      (ifmt[j], j=0...nfmt-1) .
+        //
+        //   Algorithm description:
+        //      Data processed inside while(ib < nwrd) loop, where 'ib' is iarr[] index;
+        //      loop breaks when 'ib' reaches the number of elements in iarr[]
+        //
+        //   Timmer's note: nwrd = # of 32 bit words in data,
+        //                  but that's now been replaced by the
+        //                  num of bytes since data may not be an
+        //                  even multiple of 4 bytes.
+
+        while (srcIndex < srcEndIndex) {
+
+            if (debug) System.out.println(String.format("+++ %d %d\n", srcIndex, srcEndIndex));
+
+            // get next format code
+            while (true) {
+                imt++;
+                // end of format statement reached, back to iterm - last parenthesis or format begining
+                if (imt > nfmt) {
+                    //imt = iterm;
+                    imt = 0;
+                    if (debug) System.out.println("1\n");
+                }
+                // meet right parenthesis, so we're finished processing format(s) in parenthesis
+                else if (ifmt.get(imt-1) == 0) {
+                    // increment counter
+                    lv[lev-1].irepeat++;
+
+                    // if format in parenthesis was processed the required number of times
+                    if (lv[lev-1].irepeat >= lv[lev-1].nrepeat) {
+                        // store left parenthesis index minus 1
+                        iterm = lv[lev-1].left - 1;
+
+                        // If end of format, will start from format index imt=iterm.
+                        // By default we continue from the beginning of the format (iterm=0).
+
+                        // done with this level - decrease parenthesis level
+                        lev--;
+
+                        if (debug) System.out.println("2\n");
+                    }
+                    // go for another round of processing by setting 'imt' to the left parenthesis
+                    else {
+                        // points ifmt[] index to the left parenthesis from the same level 'lev'
+                        imt = lv[lev-1].left;
+                        if (debug) System.out.println("3\n");
+                    }
+                }
+                else {
+                    // how many times to repeat format code
+                    ncnf = ifmt.get(imt-1)/16;
+                    // format code
+                    kcnf = ifmt.get(imt-1) - 16*ncnf;
+
+                    // left parenthesis, SPECIAL case: #repeats must be taken from data
+                    if (kcnf == 15) {
+                        // set it to regular left parenthesis code
+                        kcnf = 0;
+                        // read "N" value from buffer
+                        int i = srcBuf.getInt(srcIndex);
+
+                        // if buffer is big endian, don't swap to use its value
+                        if (!fromLittleEndian) ncnf = i;
+
+                        // put swapped val back into buffer
+                        // (destBuf automagically swaps in the putInt)
+                        destBuf.putInt(destIndex, i);
+
+                        // if swapping to big endian, use its swapped value
+                        if (fromLittleEndian) ncnf = Integer.reverseBytes(i);
+
+                        srcIndex  += 4;
+                        destIndex += 4;
+
+                        if (debug) System.out.println("ncnf from data = " + ncnf);
+                    }
+
+                    // left parenthesis - set new lv[]
+                    if (kcnf == 0) {
+                        // store ifmt[] index
+                        lv[lev].left = imt;
+                        // how many time will repeat format code inside parenthesis
+                        lv[lev].nrepeat = ncnf;
+                        // cleanup place for the right parenthesis (do not get it yet)
+                        lv[lev].irepeat = 0;
+                        // increase parenthesis level
+                        lev++;
+
+                        if (debug) System.out.println( "4\n");
+                    }
+                    // format F or I or ...
+                    else {
+                        // there are no parenthesis, just simple format, go to processing
+                        if (lev == 0) {
+                            if (debug) System.out.println("51\n");
+                        }
+                        // have parenthesis, NOT the pre-last format element (last assumed ')' ?)
+                        else if (imt != (nfmt-1)) {
+                            if (debug) System.out.println(String.format("52: %d %d\n",imt,nfmt-1));
+                        }
+                        // have parenthesis, NOT the first format after the left parenthesis
+                        else if (imt != lv[lev-1].left+1) {
+                            if (debug) System.out.println(String.format("53: %d %d\n",imt,nfmt-1));
+                        }
+                        else {
+                            // If none of above, we are in the end of format
+                            // so set format repeat to a big number.
+                            ncnf = 999999999;
+                            if (debug) System.out.println( "54\n");
+                        }
+                        break;
+                    }
+                }
+            }
+
+
+            // if 'ncnf' is zero, get "N" from data (always in 'int' format)
+            if (ncnf == 0) {
+                // read "N" value from buffer
+                int i = srcBuf.getInt(srcIndex);
+
+                // if buffer is big endian, don't swap to use its value
+                if (!fromLittleEndian) ncnf = i;
+
+                // put swapped val back into buffer
+                // (destBuf automagically swaps in the putInt)
+                destBuf.putInt(destIndex, i);
+
+                // if swapping to big endian, use its swapped value
+                if (fromLittleEndian) ncnf = Integer.reverseBytes(i);
+
+                srcIndex  += 4;
+                destIndex += 4;
+
+                if (debug) System.out.println("ncnf from data = " + ncnf);
+            }
+
+
+            // At this point we are ready to process next piece of data;.
+            // We have following entry info:
+            //     kcnf - format type
+            //     ncnf - how many times format repeated
+            //     b8   - starting data pointer (it comes from previously processed piece)
+
+            // Convert data in according to type kcnf
+
+            // If 64-bit
+            if (kcnf == 8 || kcnf == 9 || kcnf == 10) {
+                long i;
+                int b64EndIndex = srcIndex + 8*ncnf;
+                // make sure we don't go past end of data
+                if (b64EndIndex > srcEndIndex) b64EndIndex = srcEndIndex;
+                // swap all 64 bit items
+                while (srcIndex < b64EndIndex) {
+                    // buffers are of opposite endianness so putLong will automagically swap
+                    destBuf.putLong(destIndex, srcBuf.getLong(srcIndex));
+                    srcIndex  += 8;
+                    destIndex += 8;
+                }
+
+                if (debug) System.out.println("64bit: %d elements " + ncnf);
+            }
+            // 32-bit
+            else if (kcnf == 1 || kcnf == 2 || kcnf == 11 || kcnf == 12) {
+                int i, b32EndIndex = srcIndex + 4*ncnf;
+                // make sure we don't go past end of data
[truncated at 1000 lines; 799 more skipped]

jevio-base/src/main/java/org/jlab/coda/jevio
DataType.java 1.2 -> 1.3
diff -N DataType.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ DataType.java	28 Feb 2012 19:41:36 -0000	1.3
@@ -0,0 +1,117 @@
+package org.jlab.coda.jevio;
+
+/**
+ * This an enum used to convert data type numerical values to a more meaningful name. For example, the data type with
+ * value 0xe corresponds to the enum BANK. Mostly this is used for printing. In this version of evio, the
+ * ALSOTAGSEGMENT (0x40) value was removed from this enum because the upper 2 bits of a byte containing
+ * the datatype are now used to store padding data.
+ * 
+ * @author heddle
+ * 
+ */
+public enum DataType {
+
+	UNKNOWN32      (0x0), 
+	UINT32         (0x1), 
+	FLOAT32        (0x2), 
+	CHARSTAR8      (0x3), 
+	SHORT16        (0x4), 
+	USHORT16       (0x5), 
+	CHAR8          (0x6), 
+	UCHAR8         (0x7), 
+	DOUBLE64       (0x8), 
+	LONG64         (0x9), 
+	ULONG64        (0xa), 
+	INT32          (0xb), 
+	TAGSEGMENT     (0xc), 
+	SEGMENT        (0xd), 
+    BANK           (0xe),
+    COMPOSITE      (0xf),
+	ALSOBANK       (0x10),
+	ALSOSEGMENT    (0x20),
+    // This is only used when dealing with COMPOSITE data.
+    // It is never transported independently.
+    HOLLERIT       (0x21);
+//  Remove ALSOTAGSEGMENT (0x40) since it was never
+//  used and we now need that to store padding data.
+//	ALSOTAGSEGMENT (0x40); 
+	private int value;
+
+	private DataType(int value) {
+		this.value = value;
+	}
+
+	/**
+	 * Get the enum's value.
+	 * 
+	 * @return the value, e.g., 0xe for a BANK
+	 */
+	public int getValue() {
+		return value;
+	}
+
+	/**
+	 * Obtain the name from the value.
+	 * 
+	 * @param value the value to match.
+	 * @return the name, or "UNKNOWN".
+	 */
+	public static String getName(int value) {
+		DataType datatypes[] = DataType.values();
+		for (DataType dt : datatypes) {
+			if (dt.value == value) {
+				return dt.name();
+			}
+		}
+
+//        // for historical, backwards-compatibility reasons
+//        if (value == 0x40) {
+//            return "TAGSEGMENT";
+//        }
+
+		return "UNKNOWN";
+	}
+
+	/**
+	 * Obtain the enum from the value.
+     * 
+	 * 
+	 * @param value the value to match.
+	 * @return the matching enum, or <code>null</code>.
+	 */
+	public static DataType getDataType(int value) {
+		DataType datatypes[] = DataType.values();
+		for (DataType dt : datatypes) {
+			if (dt.value == value) {
+				return dt;
+			}
+		}
+
+//        // for historical, backwards-compatibility reasons
+//        if (value == 0x40) {
+//            return TAGSEGMENT;
+//        }
+
+		return null;
+	}
+	
+	/**
+	 * Convenience routine to see if "this" data type is a structure (a container.)
+	 * @return <code>true</code> if the data type corresponds to one of the structure
+	 * types: BANK, SEGMENT, or TAGSEGMENT.
+	 */
+	public boolean isStructure() {
+		switch (this) {
+		case BANK:
+		case SEGMENT:
+		case TAGSEGMENT:
+			return true;
+		case ALSOBANK:
+		case ALSOSEGMENT:
+//		case ALSOTAGSEGMENT:
+			return true;
+		default:
+			return false;
+		}
+	}
+}

jevio-base/src/main/java/org/jlab/coda/jevio
Demo.java 1.2 -> 1.3
diff -N Demo.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ Demo.java	28 Feb 2012 19:41:36 -0000	1.3
@@ -0,0 +1,127 @@
+package org.jlab.coda.jevio;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+/**
+ * Used for demoing the JEvio library.
+ * 
+ * @author heddle
+ * 
+ */
+public class Demo implements IEvioListener {
+
+	/**
+	 * Main program for testing
+	 * 
+	 * @param args the command line arguments.
+	 */
+	public static void main(String args[]) {
+		System.out.println("Test of EvioFile class.");
+
+		String cwd = Environment.getInstance().getCurrentWorkingDirectory();
+
+		// assumes we have a directory "testdata" with the test files in the cwd.
+//		String testEventFile = cwd + "\\testdata\\dennis.ev";
+//		String testEventFile = cwd + "\\testdata\\out.ev";
+//		String testDictFile = cwd + "\\testdata\\eviodict.xml";
+        String testEventFile = cwd + File.separator + "testdata" + File.separator + "out.ev";
+        String testDictFile  = cwd + File.separator + "testdata" + File.separator + "eviodict.xml";
+
+		EvioFile evioFile = null;
+
+		try {
+			File file = new File(testEventFile);
+			System.out.println("ev file: " + testEventFile + " size: " + file.length());
+
+			evioFile = new EvioFile(testEventFile);
+			
+			// uncomment below to test a filter
+//			IEvioFilter myFilter = new IEvioFilter() {
+//				@Override
+//				public boolean accept(StructureType structureType, BaseStructureHeader structureHeader) {
+//					DataType dataType = structureHeader.getDataTypeEnum();
+//					
+//					return (dataType == DataType.DOUBLE64);
+//				}
+//			};
+//			EventParser.getInstance().setEvioFilter(myFilter);
+
+			
+			//add myself as a listener
+			evioFile.getParser().addEvioListener(new Demo());
+
+			// create a dictionary, set the global name provider
+			NameProvider.setProvider(NameProviderFactory.createNameProvider(new File(testDictFile)));
+
+			// try a block test
+			EvioFileTest.readAllBlockHeadersTest(evioFile);
+
+			// try read all events test
+			EvioFileTest.readAllEventsTest(evioFile);
+
+			// just count the blocks
+			EvioFileTest.totalBlockCount(evioFile);
+
+			// try processing some events
+			EvioFileTest.parseEventsTest(evioFile, 1);
+		}
+		catch (FileNotFoundException e) {
+			e.printStackTrace();
+			System.exit(1);
+		}
+		catch (IOException e) {
+			e.printStackTrace();
+			System.exit(1);
+		}
+
+	}
+
+    public void startEventParse(EvioEvent evioEvent) { }
+
+    public void endEventParse(EvioEvent evioEvent) { }
+
+	/**
+	 * This IEvioListener has received a structure as the result of an event being parsed.
+	 * @param evioEvent the event being parsed.
+	 * @param structure the structure that I received. It is a BANK, SEGEMENT
+	 * or TAGSEGMENT.
+	 */
+	@Override
+	public void gotStructure(EvioEvent evioEvent, IEvioStructure structure) {
+
+		BaseStructureHeader header = structure.getHeader();
+
+		System.out.println("------------------");
+		System.out.println("" + structure);
+		
+		switch (header.getDataType()) {
+		case DOUBLE64:
+			System.out.println("        DOUBLE VALS");
+			double doubledata[] = structure.getDoubleData();
+			for (double d : doubledata) {
+				System.out.println("         " + d);
+			}
+			break;
+			
+		case INT32: case UINT32:
+			System.out.println("        INT VALS");
+			int intdata[] = structure.getIntData();
+			for (int i : intdata) {
+				System.out.println("         " + i);
+			}
+			break;
+			
+		case CHAR8: case UCHAR8:
+			System.out.println("        BYTE VALS");
+			byte bytedata[] = structure.getByteData();
+			for (byte i : bytedata) {
+				System.out.println("         " + i);
+			}
+			break;
+		}
+
+	}
+
+}

jevio-base/src/main/java/org/jlab/coda/jevio
Environment.java 1.2 -> 1.3
diff -N Environment.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ Environment.java	28 Feb 2012 19:41:36 -0000	1.3
@@ -0,0 +1,203 @@
+package org.jlab.coda.jevio;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ * This is a utility class employing a singleton to obtain environment information, such as user name, home directory,
+ * OS name, etc.
+ * 
+ * @author heddle
+ * 
+ */
+public class Environment {
+
+	/**
+	 * Singleton instantiation.
+	 */
+	private static Environment environment;
+
+	/**
+	 * User's home directory.
+	 */
+
+	private String homeDirectory;
+
+	/**
+	 * Current working directory
+	 */
+
+	private String currentWorkingDirectory;
+
+	/**
+	 * User name
+	 */
+
+	private String userName;
+
+	/**
+	 * Operating System Name
+	 */
+
+	private String osName;
+
+	/**
+	 * Temporary Directory
+	 */
+
+	private String tempDirectory;
+
+	/**
+	 * Class path.
+	 */
+	private String classPath;
+
+	/**
+	 * The host name.
+	 */
+	private String hostName;
+
+	/**
+	 * Private constructor for the singleton.
+	 */
+	private Environment() {
+		homeDirectory = getProperty("user.home");
+		currentWorkingDirectory = getProperty("user.dir");
+		userName = getProperty("user.name");
+		osName = getProperty("os.name");
+		tempDirectory = getProperty("java.io.tmpdir");
+		classPath = getProperty("java.class.path");
+
+		try {
+			InetAddress addr = InetAddress.getLocalHost();
+
+			// Get hostname
+			hostName = addr.getHostName();
+		}
+		catch (UnknownHostException e) {
+			hostName = "???";
+		}
+
+	}
+
+	/**
+	 * Accessor for the singleton.
+	 * 
+	 * @return the singleton <code>Environment</code> object.
+	 */
+	public static Environment getInstance() {
+		if (environment == null) {
+			environment = new Environment();
+		}
+		return environment;
+	}
+
+	/**
+	 * Convenience routine for getting a system property.
+	 * 
+	 * @param keyName the key name of the property
+	 * @return the property, or <code>null</null>.
+	 */
+	private String getProperty(String keyName) {
+		try {
+			return System.getProperty(keyName);
+		}
+		catch (Exception e) {
+			return null;
+		}
+	}
+
+	/**
+	 * Get the current class path.
+	 * 
+	 * @return the current class path.
+	 */
+	public String getClassPath() {
+		return classPath;
+	}
+
+	/**
+	 * Get the current working directory.
+	 * 
+	 * @return the current WorkingDirectory.
+	 */
+	public String getCurrentWorkingDirectory() {
+		return currentWorkingDirectory;
+	}
+
+	/**
+	 * Get the user's home directory.
+	 * 
+	 * @return the home directory.
+	 */
+	public String getHomeDirectory() {
+		return homeDirectory;
+	}
+
+	/**
+	 * Get the operating system name.
+	 * 
+	 * @return the operating system name.
+	 */
+	public String getOsName() {
+		return osName;
+	}
+
+	/**
+	 * Get the temp directory.
+	 * 
+	 * @return the temp directory.
+	 */
+	public String getTempDirectory() {
+		return tempDirectory;
+	}
+
+	/**
+	 * Get the user name.
+	 * 
+	 * @return the userName.
+	 */
+	public String getUserName() {
+		return userName;
+	}
+
+	/**
+	 * Get the host name.
+	 * 
+	 * @return the host name.
+	 */
+	public String getHostName() {
+		return hostName;
+	}
+
+	/**
+	 * Convert to a string for diagnostics
+	 * 
+	 * @return The string
+	 */
+
+	@Override
+	public String toString() {
+		StringBuffer sb = new StringBuffer(1024);
+		sb.append("Environment: \n");
+		sb.append("Host Name: " + getHostName() + "\n");
+		sb.append("User Name: " + getUserName() + "\n");
+		sb.append("Temp Directory: " + getTempDirectory() + "\n");
+		sb.append("OS Name: " + getOsName() + "\n");
+		sb.append("Home Directory: " + getHomeDirectory() + "\n");
+		sb.append("Current Working Directory: " + getCurrentWorkingDirectory() + "\n");
+		sb.append("Class Path: " + getClassPath() + "\n");
+		return sb.toString();
+	}
+
+	/**
+	 * Main program used for testing only.
+	 * 
+	 * @param args
+	 */
+	public static void main(String[] args) {
+		Environment env = Environment.getInstance();
+		System.out.println(env.toString());
+	}
+
+}

jevio-base/src/main/java/org/jlab/coda/jevio
EventBuilder.java 1.2 -> 1.3
diff -N EventBuilder.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ EventBuilder.java	28 Feb 2012 19:41:36 -0000	1.3
@@ -0,0 +1,452 @@
+package org.jlab.coda.jevio;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * This class is used for creating and manipulating events. One constructor is convenient for creating new events while
+ * another is useful for manipulating existing events. You can create a new EventBuilder for each event being handled,
+ * However, in many cases one can use the same EventBuilder for all events by calling the setEvent method.
+ * The only reason a singleton pattern was not used was to allow for the possibility that events will be built or
+ * manipulated on multiple threads.
+ * @author heddle
+ *
+ */
+public class EventBuilder {
+
+	/**
+	 * The event being built
+	 */
+	private EvioEvent event;
+	
+	
+	/**
+	 * This is the constructor to use for an EventBuilder object that will operate on a new, empty event.
+	 * @param tag the tag for the event header (which is just a bank header).
+	 * @param dataType the data type for the event object--which again is just the type for the outer most
+	 * bank. Often an event is a bank of banks, so typically this will be DataType.BANK, or 0xe (14).
+	 * @param num often an ordinal enumeration.
+	 */
+	public EventBuilder(int tag, DataType dataType, int num) {
+		//create an event with the correct header data. 
+		event = new EvioEvent(tag, dataType, num);
+	}
+	
+	/**
+	 * This is the constructor to use when you want to manipulate an existing event.
+	 * @param event the event to manipulate.
+	 */
+	public EventBuilder(EvioEvent event) {
+		this.event = event;
+	}
+	
+	/**
+	 * This goes through the event recursively, and makes sure all the length fields
+	 * in the headers are properly set.
+	 */
+	public void setAllHeaderLengths() {
+		event.setAllHeaderLengths();
+	}
+		
+	/**
+	 * This clears all the data fields in a structure, but not the parent or the children. This keeps the
+	 * existing tree structure intact. To remove a structure (and, consequently, all its descendants) from the
+	 * tree, use <code>remove</code>
+	 * @param structure the segment to clear.
+	 */
+	public void clearData(BaseStructure structure) {
+		if (structure != null) {
+			structure.rawBytes    = null;
+			structure.doubleData  = null;
+			structure.floatData   = null;
+			structure.intData     = null;
+			structure.longData    = null;
+            structure.shortData   = null;
+            structure.charData    = null;
+            structure.stringData  = null;
+            structure.stringsList = null;
+            structure.stringEnd   = 0;
+            structure.numberDataItems = 0;
+		}
+	}
+	
+	/**
+	 * Add a child to a parent structure.
+     *
+	 * @param parent the parent structure.
+	 * @param child the child structure.
+	 * @throws EvioException if child is null, 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.getByteOrder() != event.getByteOrder()) {
+            throw new EvioException("Attempt to add child with opposite byte order.");
+        }
+		
+		// the child must be consistent with the data type of the parent. For example, if the child
+		// is a BANK, then the data type of the parent must be BANK.
+		DataType parentDataType = parent.header.getDataType();
+		String errStr;
+		if (parentDataType.isStructure()) {
+			switch(parentDataType) {
+			case BANK: case ALSOBANK:
+				if (child.getStructureType() != StructureType.BANK) {
+					errStr = "Type mismatch in addChild. Parent content type: " + parentDataType + 
+					" child type: " + child.getStructureType();
+					throw new EvioException(errStr);
+				}
+				break;
+				
+			case SEGMENT: case ALSOSEGMENT:
+				if (child.getStructureType() != StructureType.SEGMENT) {
+					errStr = "Type mismatch in addChild. Parent content type: " + parentDataType + 
+					" child type: " + child.getStructureType();
+					throw new EvioException(errStr);
+				}
+				break;
+				
+			case TAGSEGMENT:// case ALSOTAGSEGMENT:
+				if (child.getStructureType() != StructureType.TAGSEGMENT) {
+					errStr = "Type mismatch in addChild. Parent content type: " + parentDataType + 
+					" child type: " + child.getStructureType();
+					throw new EvioException(errStr);
+				}
+				break;
+			}
+		}
+		else { //parent is not a container--it is expecting to hold primitives and cannot have children
+			errStr = "Type mismatch in addChild. Parent content type: " + parentDataType + 
+			" cannot have children.";
+			throw new EvioException(errStr);
+		}
+		
+
+		parent.insert(child);
+		setAllHeaderLengths();
+	}
+	
+	/**
+	 * This removes a structure (and all its descendants) from the tree.
+	 * @param child the child structure to remove.
+	 * @throws EvioException
+	 */
+	public void remove(BaseStructure child) throws EvioException {
+		if (child == null) {
+			throw new EvioException("Attempt to remove null child.");
+		}
+		
+		BaseStructure parent = child.getParent(); 
+		
+		//the only orphan structure is the event itself, which cannot be removed.
+		if (parent == null) {
+			throw new EvioException("Attempt to remove root node, i.e., the event. Don't remove an event. Just discard it.");
+		}
+				
+		child.removeFromParent();
+		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();
+	}
+	
+	/**
+	 * Appends short 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 short data to append, or set if there is no existing data.
+	 * @throws EvioException
+	 */
+	public void appendShortData(BaseStructure structure, short data[]) throws EvioException {
+		if (structure == null) {
+			throw new EvioException("Tried to append short data to a null structure.");
+		}
+		structure.appendShortData(data);
+		setAllHeaderLengths();
+	}
+
+	
+	/**
+	 * Appends long 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 long data to append, or set if there is no existing data.
+	 * @throws EvioException
+	 */
+	public void appendLongData(BaseStructure structure, long data[]) throws EvioException {
+		if (structure == null) {
+			throw new EvioException("Tried to append long data to a null structure.");
+		}
+		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.
+	 * @param structure the structure to receive the data, which is appended.
+	 * @param data the byte data to append, or set if there is no existing data.
+	 * @throws EvioException
+	 */
+	public void appendByteData(BaseStructure structure, byte data[]) throws EvioException {
+		if (structure == null) {
+			throw new EvioException("Tried to append byte data to a null structure.");
+		}
+		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.
+	 * @param structure the structure to receive the data, which is appended.
+	 * @param data the float data to append, or set if there is no existing data.
+	 * @throws EvioException
+	 */
+	public void appendFloatData(BaseStructure structure, float data[]) throws EvioException {
+		if (structure == null) {
+			throw new EvioException("Tried to append float data to a null structure.");
+		}
+		structure.appendFloatData(data);
+		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 string 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 string to append, or set if there is no existing data.
+     * @throws EvioException
+     */
+    public void appendStringData(BaseStructure structure, String data) throws EvioException {
+
+        if (structure == null) {
+            throw new EvioException("Tried to append String to a null structure.");
+        }
+        structure.appendStringData(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();
+	}
+	
+	/**
+	 * Get the underlying event.
+	 * @return the underlying event.
+	 */
+	public EvioEvent getEvent() {
+		return event;
+	}
+	
+	/**
+	 * Set the underlying event. As far as this event builder is concerned, the
+	 * previous underlying event is lost, and all subsequent calls will affect
+	 * the newly supplied event.
+	 * param the new underlying event.
+	 */
+	public void setEvent(EvioEvent event) {
+		this.event = event;
+	}
+	
+	/**
+	 * Main program for testing.
+	 * @param args ignored command line arguments.
+	 */
+	public static void main(String args[]) {
+		//create an event writer to write out the test events.
+		String outfile = "C:\\Documents and Settings\\heddle\\My Documents\\test.ev";
+		EventWriter eventWriter = null;
+		try {
+			eventWriter = new EventWriter(new File(outfile));
+		}
+		catch (EvioException e) {
+			e.printStackTrace();
+			System.exit(1);
+		}
+		
+		//count the events we make for testing
+		int eventNumber = 1;
+		
+		//use a tag of 11 for events--for no particular reason
+		int tag = 11;
+		
+		try {
+			//first event-- a trivial event containing an array of ints. 
+			EventBuilder eventBuilder = new EventBuilder(tag, DataType.INT32, eventNumber++);
+			EvioEvent event1 = eventBuilder.getEvent();
+			//should end up with int array 1..25,1..10
+			eventBuilder.appendIntData(event1, fakeIntArray(25));
+			eventBuilder.appendIntData(event1, fakeIntArray(10));
+			eventWriter.writeEvent(event1);
+			
+			//second event, more traditional bank of banks
+			eventBuilder = new EventBuilder(tag, DataType.BANK, eventNumber++);
+			EvioEvent event2 = eventBuilder.getEvent();
+			
+			//add a bank of doubles
+			EvioBank bank1 = new EvioBank(22, DataType.DOUBLE64, 0);
+			eventBuilder.appendDoubleData(bank1, fakeDoubleArray(10));
+			eventBuilder.addChild(event2, bank1);
+			eventWriter.writeEvent(event2);
+			
+			//lets modify event2
+			event2.getHeader().setNumber(eventNumber++);
+			EvioBank bank2 = new EvioBank(33, DataType.BANK, 0);
+			eventBuilder.addChild(event2, bank2);
+
+			EvioBank subBank1 = new EvioBank(34, DataType.SHORT16, 1);
+			eventBuilder.addChild(bank2, subBank1);
+			eventBuilder.appendShortData(subBank1, fakeShortArray(5));
+			
+						
+			
+			//now add a bank of segments
+			EvioBank subBank2 = new EvioBank(33, DataType.SEGMENT, 0);
+			eventBuilder.addChild(bank2, subBank2);
+			
+			EvioSegment segment1 = new EvioSegment(34, DataType.SHORT16);
+			eventBuilder.addChild(subBank2, segment1);
+			eventBuilder.appendShortData(segment1, fakeShortArray(7));
+			
+			EvioSegment segment2 = new EvioSegment(34, DataType.SHORT16);
+			eventBuilder.addChild(subBank2, segment2);
+			eventBuilder.appendShortData(segment2, fakeShortArray(10));
+			
+			
+			//now add a bank of tag segments
+			EvioBank subBank3 = new EvioBank(45, DataType.TAGSEGMENT, 0);
+			eventBuilder.addChild(bank2, subBank3);
+
+			EvioTagSegment tagsegment1 = new EvioTagSegment(34, DataType.INT32);
+			eventBuilder.addChild(subBank3, tagsegment1);
+			eventBuilder.appendIntData(tagsegment1, fakeIntArray(3));
+			
+			EvioTagSegment tagsegment2 = new EvioTagSegment(34, DataType.CHARSTAR8);
+			eventBuilder.addChild(subBank3, tagsegment2);
+			eventBuilder.appendStringData(tagsegment2, "This is a string");
+
+			
+//			System.err.println("EVENT2: " + event2.getHeader());
+//			System.err.println("BANK1: " + bank1.getHeader());
+//			System.err.println("BANK2: " + bank2.getHeader());
+//			System.err.println("SUBBANK1: " + subBank1.getHeader());
+//			System.err.println("SUBBANK2: " + subBank2.getHeader());
+//			System.err.println("segment1: " + segment1.getHeader());
+//			System.err.println("segment2: " + segment2.getHeader());
+//			System.err.println("SUBBANK3: " + subBank3.getHeader());
+//			System.err.println("tagseg1: " + tagsegment1.getHeader());
+			
+			
+			//write the event
+			eventWriter.writeEvent(event2);		
+			
+            //all done
+            eventWriter.close();
+
+		}
+        catch (EvioException e) {
+            e.printStackTrace();
+        }
+        catch (IOException e) {
+            e.printStackTrace();
+        }
+
+		System.out.println("Test completed.");
+	}
+	
+	/**
+	 * Array of ints, sequential, 1..size, for test purposes.
+	 * @param size the size of the array.
+	 * @return the fake int array.
+	 */
+	private static int[] fakeIntArray(int size) {
+		int[] array = new int[size];
+		for (int i = 0; i < size; i++) {
+			array[i] = i+1;
+		}
+		return array;
+	}
+	
+	/**
+	 * Array of shorts, sequential, 1..size, for test purposes.
+	 * @param size the size of the array.
+	 * @return the fake short array.
+	 */
+	private static short[] fakeShortArray(int size) {
+		short[] array = new short[size];
+		for (int i = 0; i < size; i++) {
+			array[i] = (short)(i+1);
+		}
+		return array;
+	}
+	
+	/**
+	 * Array of characters for test purposes.
+	 * @return the fake char array.
+	 */
+	private static char[] fakeCharArray() {
+		char array[] = {'T','h','i','s',' ','i','s',' ','c','h','a','r',' ', 'd','a','t','a','.'};
+		return array;
+	}
+
+
+	
+	/**
+	 * Array of doubles, sequential, 1..size, for test purposes.
+	 * @param size the size of the array.
+	 * @return the fake double array.
+	 */
+	private static double[] fakeDoubleArray(int size) {
+		double[] array = new double[size];
+		for (int i = 0; i < size; i++) {
+			array[i] = i+1;
+		}
+		return array;
+	}
+
+}

jevio-base/src/main/java/org/jlab/coda/jevio
EventParser.java 1.2 -> 1.3
diff -N EventParser.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ EventParser.java	28 Feb 2012 19:41:36 -0000	1.3
@@ -0,0 +1,614 @@
+package org.jlab.coda.jevio;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+import javax.swing.event.EventListenerList;
+import javax.swing.tree.DefaultTreeModel;
+
+/**
+ * Creates an object that controls the parsing of events.
+ * This object, like the EvioReader object, has a method for parsing an event. An EvioReader
+ * object will ultimately call this method--i.e., the concrete implementation of event
+ * parsing is in this class.
+ * 
+ * @author heddle
+ * @author timmer
+ */
+public class EventParser {
+
+	/**
+	 * Listener list for structures (banks, segments, tagsegments) encountered while processing an event.
+	 */
+	private EventListenerList evioListenerList;
+
+	/**
+	 * This flag determines whether notification of listeners is active. Normally it is. But in some cases it should be
+	 * temporarily suspended. For example, in a "goto event" process, the listeners will not be notified of the
+	 * intervening events as the file is scanned to get to the target event.
+	 */
+	protected boolean notificationActive = true;
+
+	/**
+	 * A single global structure filter. This may prove to be a limitation: perhaps we want one per listener.
+	 */
+	private IEvioFilter evioFilter;
+
+	/**
+	 * This is the workhorse method for parsing the event. It will drill down and uncover all structures
+     * (banks, segments, and tagsegments) and notify any interested listeners in a SAX-Like manner. <br>
+	 * Note: applications may choose not to provide a listener. In that case, when the event is parsed, its structures
+	 * may be accessed through the event's tree model, i.e., via <code>event.getTreeModel()</code>.
+	 * 
+	 * @param evioEvent the event to parse.
+	 * @throws EvioException
+	 */
+	public synchronized void parseEvent(EvioEvent evioEvent) throws EvioException {
+
+		if (evioEvent == null) {
+			throw new EvioException("Null event in parseEvent.");
+		}
+
+        //let listeners know we started
+        notifyStart(evioEvent);
+
+		// The event itself is a stucture (EvioEvent extends EvioBank) so just
+		// parse it as such. The recursive drill down will take care of the rest.
+		parseStructure(evioEvent, evioEvent);
+
+        //let listeners know we stopped
+        notifyStop(evioEvent);        
+	}
+
+	/**
+	 * Parse a structure. If it is a structure of structures, such as a bank of banks or a segment of tag segments,
+	 * parse recursively. Listeners are notified AFTER all their children have been handled, not before. Thus the
+	 * LAST structure that will send notification is the outermost bank--the event bank itself.
+	 * 
+	 * @param evioEvent the parent event being processed.
+	 * @param structure the structure being processed.
+	 * @throws EvioException
+	 */
+	private void parseStructure(EvioEvent evioEvent, BaseStructure structure) throws EvioException {
+
+		// update the tree
+		BaseStructure child = structure;
+		BaseStructure parent = child.getParent();
+
+		if (parent == null) { // start of event
+			evioEvent.treeModel = new DefaultTreeModel(evioEvent);
+		}
+		else {
+			evioEvent.insert(child, parent);
+		}
+
+		// 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
+			notifyEvioListeners(evioEvent, structure);
+			return;
+		}
+
+		// Does the present structure contain structures? (as opposed to a leaf, which contains primitives). If
+		// it is a leaf 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.
+        byte bytes[] = structure.getRawBytes();
+        ByteOrder byteOrder = structure.getByteOrder();
+
+        if (bytes == null) {
+            if (evioEvent == null) {
+                throw new EvioException("Null data in parseStructure (Bank).");
+            }
+        }
+
+        int length = bytes.length;
+        int offset = 0;
+
+        switch (dataType) {
+            case BANK:
+            case ALSOBANK:
+
+                // extract all the banks from this bank.
+                while (offset < length) {
+                    BankHeader header = createBankHeader(bytes, offset, byteOrder);
+
+                    // offset still points to beginning of new header. Have to get data for new child bank.
+                    int newByteLen = 4 * (header.getLength() - 1); // -1 to account for extra header word
+                    byte newBytes[] = new byte[newByteLen];
+                    System.arraycopy(bytes, offset + 8, newBytes, 0, newByteLen);
+
+                    // we can now create a new bank
+                    EvioBank childBank = new EvioBank(header);
+                    childBank.setParent(structure);
+
+                    childBank.setRawBytes(newBytes);
+                    childBank.setByteOrder(byteOrder);
+                    parseStructure(evioEvent, childBank);
+
+                    // position offset to start of next header
+                    offset += 4 * (header.getLength() + 1); // plus 1 for length word
+                }
+                break; // structure contains banks
+
+            case SEGMENT:
+            case ALSOSEGMENT:
+
+                // extract all the segments from this bank.
+                while (offset < length) {
+                    SegmentHeader header = createSegmentHeader(bytes, offset, byteOrder);
+
+                    // offset still points to beginning of new header. Have to get data for new child segment.
+                    int newByteLen = 4 * header.getLength();
+                    byte newBytes[] = new byte[newByteLen];
+                    System.arraycopy(bytes, offset + 4, newBytes, 0, newByteLen);
+
+                    // we can now create a new segment
+                    EvioSegment childSegment = new EvioSegment(header);
+                    childSegment.setParent(structure);
+
+                    childSegment.setRawBytes(newBytes);
+                    childSegment.setByteOrder(byteOrder);
+                    parseStructure(evioEvent, childSegment);
+
+                    // position offset to start of next header
+                    offset += 4 * (header.getLength() + 1); // plus 1 for length word
+                }
+
+                break; // structure contains segments
+
+            case TAGSEGMENT:
+//			case ALSOTAGSEGMENT:
+
+                // extract all the tag segments from this bank.
+                while (offset < length) {
+                    TagSegmentHeader header = createTagSegmentHeader(bytes, offset, byteOrder);
+
+                    // offset still points to beginning of new header. Have to get data for new child tag segment.
+                    int newByteLen = 4 * header.getLength();
+                    byte newBytes[] = new byte[newByteLen];
+                    System.arraycopy(bytes, offset + 4, newBytes, 0, newByteLen);
+
+                    // we can now create a new tag segment
+                    EvioTagSegment childTagSegment = new EvioTagSegment(header);
+                    childTagSegment.setParent(structure);
+
+                    childTagSegment.setRawBytes(newBytes);
+                    childTagSegment.setByteOrder(byteOrder);
+                    parseStructure(evioEvent, childTagSegment);
+
+                    // position offset to start of next header
+                    offset += 4 * (header.getLength() + 1); // plus 1 for length word
+                }
+
+                break;
+        }
+        // notify the listeners
+		notifyEvioListeners(evioEvent, structure);
+	} // parseStructure
+
+	/**
+	 * Create a bank header from the first eight bytes of the data array.
+	 * 
+	 * @param bytes the byte array, probably from a bank that encloses this new bank.
+	 * @param offset the offset to start reading from the byte array.
+     * @param byteOrder byte order of array, {@link ByteOrder#BIG_ENDIAN} or {@link ByteOrder#LITTLE_ENDIAN}
+	 * @return the new header.
+	 */
+	static BankHeader createBankHeader(byte bytes[], int offset, ByteOrder byteOrder) {
+		BankHeader header = new BankHeader();
+
+        try {
+            header.setLength(ByteDataTransformer.toInt(bytes, byteOrder, offset));
+            offset += 4;
+
+            if (byteOrder == ByteOrder.BIG_ENDIAN) {
+                // interested in bit pattern, not negative numbers
+                header.setTag(ByteDataTransformer.shortBitsToInt(
+                                ByteDataTransformer.toShort(bytes, byteOrder, offset)));
+                offset += 2;
+                int dt = ByteDataTransformer.byteBitsToInt(bytes[offset]);
+                offset++;
+                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);
+                header.setNumber(ByteDataTransformer.byteBitsToInt(bytes[offset]));
+            }
+            else {
+                header.setNumber(ByteDataTransformer.byteBitsToInt(bytes[offset]));
+                offset++;
+                int dt = ByteDataTransformer.byteBitsToInt(bytes[offset]);
+                offset++;
+                int type = dt & 0x3f;
+                int padding = dt >>> 6;
+                if (dt == 0x40) {
+                    type = DataType.TAGSEGMENT.getValue();
+                    padding = 0;
+                }
+                header.setDataType(type);
+                header.setPadding(padding);
+                header.setTag(ByteDataTransformer.shortBitsToInt(
+                                ByteDataTransformer.toShort(bytes, byteOrder, offset)));
+            }
+        }
+        catch (EvioException e) {/* never happen */}
+
+        return header;
+    }
+
+    /**
+     * Create a bank header from the first eight bytes of the data array.
+     *
+     * @param bytes the byte array, probably from a bank that encloses this new bank.
+     * @param offset the offset to start reading from the byte array.
+     * @param byteOrder byte order of array, {@link ByteOrder#BIG_ENDIAN} or {@link ByteOrder#LITTLE_ENDIAN}
+     * @return the new header.
+     */
+    private BankHeader createBankHeaderOrig(byte bytes[], int offset, ByteOrder byteOrder) {
+        BankHeader header = new BankHeader();
+        ByteBuffer byteBuffer = ByteBuffer.wrap(bytes).order(byteOrder);
+        byteBuffer.position(offset);
+
+        header.setLength(byteBuffer.getInt());
+
+        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);
+            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);
+            header.setTag(ByteDataTransformer.shortBitsToInt(byteBuffer.getShort()));
+        }
+        return header;
+    }
+
+    /**
+     * Create a segment header from the first four bytes of the data array.
+     *
+     * @param bytes the byte array, probably from a bank that encloses this new segment.
+     * @param offset the offset to start reading from the byte array.
+     * @param byteOrder byte order of array, {@link ByteOrder#BIG_ENDIAN} or {@link ByteOrder#LITTLE_ENDIAN}
+     * @return the new header.
+     */
+    static SegmentHeader createSegmentHeader(byte bytes[], int offset, ByteOrder byteOrder) {
+        SegmentHeader header = new SegmentHeader();
+
+        try {
+            if (byteOrder == ByteOrder.BIG_ENDIAN) {
+                header.setTag(ByteDataTransformer.byteBitsToInt(bytes[offset]));
+                offset++;
+                int dt = ByteDataTransformer.byteBitsToInt(bytes[offset]);
+                offset++;
+                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);
+                header.setLength(ByteDataTransformer.shortBitsToInt(
+                                    ByteDataTransformer.toShort(bytes, byteOrder, offset)));
+            }
+            else {
+                header.setLength(ByteDataTransformer.shortBitsToInt(
+                                    ByteDataTransformer.toShort(bytes, byteOrder, offset)));
+                offset += 2;
+                int dt = ByteDataTransformer.byteBitsToInt(bytes[offset]);
+                offset++;
+                int type = dt & 0x3f;
+                int padding = dt >>> 6;
+                if (dt == 0x40) {
+                    type = DataType.TAGSEGMENT.getValue();
+                    padding = 0;
+                }
+                header.setDataType(type);
+                header.setPadding(padding);
+                header.setTag(ByteDataTransformer.byteBitsToInt(bytes[offset]));
+            }
+        }
+        catch (EvioException e) {/* never happen */}
+
+        return header;
+    }
+
+    /**
+     * Create a segment header from the first four bytes of the data array.
+     *
+     * @param bytes the byte array, probably from a bank that encloses this new segment.
+     * @param offset the offset to start reading from the byte array.
+     * @param byteOrder byte order of array, {@link ByteOrder#BIG_ENDIAN} or {@link ByteOrder#LITTLE_ENDIAN}
+     * @return the new header.
+     */
+    private SegmentHeader createSegmentHeaderOrig(byte bytes[], int offset, ByteOrder byteOrder) {
+        SegmentHeader header = new SegmentHeader();
+        ByteBuffer byteBuffer = ByteBuffer.wrap(bytes).order(byteOrder);
+        byteBuffer.position(offset);
+
+        if (byteOrder == ByteOrder.BIG_ENDIAN) {
+            header.setTag(ByteDataTransformer.byteBitsToInt(byteBuffer.get()));
+            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);
+            header.setLength(ByteDataTransformer.shortBitsToInt(byteBuffer.getShort()));
+        }
+        else {
+            header.setLength(ByteDataTransformer.shortBitsToInt(byteBuffer.getShort()));
+            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);
+            header.setTag(ByteDataTransformer.byteBitsToInt(byteBuffer.get()));
+        }
+
+        return header;
+    }
+
+	/**
+	 * Create a tag segment header from the first four bytes of the data array.
+	 * 
+	 * @param bytes the byte array, probably from a bank that encloses this new tag segment.
+	 * @param offset the offset to start reading from the byte array.
+     * @param byteOrder byte order of array, {@link ByteOrder#BIG_ENDIAN} or {@link ByteOrder#LITTLE_ENDIAN}
+	 * @return the new header.
+	 */
+	static TagSegmentHeader createTagSegmentHeader(byte bytes[], int offset, ByteOrder byteOrder) {
+		TagSegmentHeader header = new TagSegmentHeader();
+		int temp;
+
+        try {
+            if (byteOrder == ByteOrder.BIG_ENDIAN) {
+                temp = ByteDataTransformer.shortBitsToInt(
+                        ByteDataTransformer.toShort(bytes, byteOrder, offset));
+                offset += 2;
+                header.setTag(temp >>> 4);
+                header.setDataType(temp & 0xF);
+                header.setLength(ByteDataTransformer.shortBitsToInt(
+                        ByteDataTransformer.toShort(bytes, byteOrder, offset)));
+            }
+            else {
+                header.setLength(ByteDataTransformer.shortBitsToInt(
+                        ByteDataTransformer.toShort(bytes, byteOrder, offset)));
+                offset += 2;
+                temp = ByteDataTransformer.shortBitsToInt(
+                        ByteDataTransformer.toShort(bytes, byteOrder, offset));
+                header.setTag(temp >>> 4);
+                header.setDataType(temp & 0xF);
+            }
+        }
+        catch (EvioException e) {/* never happen */}
+
+        return header;
+	}
+
+    /**
+     * Create a tag segment header from the first four bytes of the data array.
+     *
+     * @param bytes the byte array, probably from a bank that encloses this new tag segment.
+     * @param offset the offset to start reading from the byte array.
+     * @param byteOrder byte order of array, {@link ByteOrder#BIG_ENDIAN} or {@link ByteOrder#LITTLE_ENDIAN}
+     * @return the new header.
+     */
+    private TagSegmentHeader createTagSegmentHeaderOrig(byte bytes[], int offset, ByteOrder byteOrder) {
+        TagSegmentHeader header = new TagSegmentHeader();
+        ByteBuffer byteBuffer = ByteBuffer.wrap(bytes).order(byteOrder);
+        byteBuffer.position(offset);
+
+        int temp;
+
+        if (byteOrder == ByteOrder.BIG_ENDIAN) {
+            temp = ByteDataTransformer.shortBitsToInt(byteBuffer.getShort());
+            header.setTag(temp >>> 4);
+            header.setDataType(temp & 0xF);
+            header.setLength(ByteDataTransformer.shortBitsToInt(byteBuffer.getShort()));
+        }
+        else {
+            header.setLength(ByteDataTransformer.shortBitsToInt(byteBuffer.getShort()));
+            temp = ByteDataTransformer.shortBitsToInt(byteBuffer.getShort());
+            header.setTag(temp >>> 4);
+            header.setDataType(temp & 0xF);
+        }
+
+        return header;
+    }
+
+	/**
+	 * This is when a structure is encountered while parsing an event.
+     * It notifies all listeners about the structure.
+	 * 
+     * @param evioEvent event being parsed
+     * @param structure the structure encountered, which may be a Bank, Segment, or TagSegment.
+	 */
+	protected void notifyEvioListeners(EvioEvent evioEvent, IEvioStructure structure) {
+
+		// are notifications turned off?
+		if (!notificationActive) {
+			return;
+		}
+
+		if (evioListenerList == null) {
+			return;
+		}
+
+		// if there is a global filter, lets use it.
+		if (evioFilter != null) {
+			if (!evioFilter.accept(structure.getStructureType(), structure)) {
+				return;
+			}
+		}
+
+		// Guaranteed to return a non-null array
+		Object[] listeners = evioListenerList.getListenerList();
+
+		// This weird loop is the bullet proof way of notifying all listeners.
+		for (int i = listeners.length - 2; i >= 0; i -= 2) {
+			if (listeners[i] == IEvioListener.class) {
+				((IEvioListener) listeners[i + 1]).gotStructure(evioEvent, structure);
+			}
+		}
+	}
+
+    /**
+     * Notify listeners we are starting to parse a new event
+     * @param evioEvent the event in question;
+     */
+    protected void notifyStart(EvioEvent evioEvent) {
+
+        // are notifications turned off?
+        if (!notificationActive) {
+            return;
+        }
+
+        if (evioListenerList == null) {
+            return;
+        }
+
+        // Guaranteed to return a non-null array
+        Object[] listeners = evioListenerList.getListenerList();
+
+        // This weird loop is the bullet proof way of notifying all listeners.
+        for (int i = listeners.length - 2; i >= 0; i -= 2) {
+            if (listeners[i] == IEvioListener.class) {
+                ((IEvioListener) listeners[i + 1]).startEventParse(evioEvent);
+            }
+        }
+    }
+   
+    /**
+     * Notify listeners we are done to parsing a new event
+     * @param evioEvent the event in question;
+     */
+    protected void notifyStop(EvioEvent evioEvent) {
+
+        // are notifications turned off?
+        if (!notificationActive) {
+            return;
+        }
+
+        if (evioListenerList == null) {
+            return;
+        }
+
+        // Guaranteed to return a non-null array
+        Object[] listeners = evioListenerList.getListenerList();
+
+        // This weird loop is the bullet proof way of notifying all listeners.
+        for (int i = listeners.length - 2; i >= 0; i -= 2) {
+            if (listeners[i] == IEvioListener.class) {
+                ((IEvioListener) listeners[i + 1]).endEventParse(evioEvent);
+            }
+        }
+    }
+
+	/**
+	 * Remove an Evio listener. Evio listeners listen for structures encountered when an event is being parsed.
+	 * 
+	 * @param listener The Evio listener to remove.
+	 */
+	public void removeEvioListener(IEvioListener listener) {
+
+		if ((listener == null) || (evioListenerList == null)) {
+			return;
+		}
+
+		evioListenerList.remove(IEvioListener.class, listener);
+	}
+
+	/**
+	 * Add an Evio listener. Evio listeners listen for structures encountered when an event is being parsed.
+	 * 
+	 * @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);
+	}
+
+	/**
+	 * Get the flag determining whether notification of listeners is active. Normally it is. But in some cases it should
+	 * be temporarily suspended. For example, in a "goto event" process, the listeners will not be notified of the
+	 * intervening events as the file is scanned to get to the target event.
+	 * 
+	 * @return <code>true</code> if notification of events to the listeners is active.
+	 */
+	public boolean isNotificationActive() {
+		return notificationActive;
+	}
+
+	/**
+	 * Set the flag determining whether notification of listeners is active. Normally it is. But in some cases it
+	 * should be temporarily suspended. For example, in a "goto event" process, the listeners will not be notified of
+	 * the intervening events as the file is scanned to get to the target event.
+	 * 
+	 * @param notificationActive set <code>true</code> if notification of events to the listeners is active.
+	 */
+	public void setNotificationActive(boolean notificationActive) {
+		this.notificationActive = notificationActive;
+	}
+
+	/**
+	 * Set the global filter used for filtering structures. If set to <code>null</code>, the default, then all
+	 * structures will be sent to the listeners.
+	 * 
+	 * @param evioFilter the filter to set.
+	 * @see IEvioFilter
+	 */
+	public void setEvioFilter(IEvioFilter evioFilter) {
+		this.evioFilter = evioFilter;
+	}
+
+}

jevio-base/src/main/java/org/jlab/coda/jevio
EventWriter.java 1.2 -> 1.3
diff -N EventWriter.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ EventWriter.java	28 Feb 2012 19:41:36 -0000	1.3
@@ -0,0 +1,1015 @@
+package org.jlab.coda.jevio;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.LinkedList;
+import java.util.BitSet;
+
+/**
+ * An EventWriter object is used for writing events to a file or to a byte buffer.
+ * This class does NOT write versions 1-3 data, only version 4!
+ * This class is threadsafe.
+ *
+ * @author heddle
+ * @author timmer
+ */
+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.
+     */
+    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.
+     */
+    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.
+     */
+    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.
+     */
+    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;
+
+    /**
+     * The default maximum event count for a single block used for writing.
+     */
+    private static int DEFAULT_BLOCK_COUNT_LIMIT = 200;
+
+
+
+    /**
+     * 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.
+     */
+    private int blockSizeMax;
+
+    /**
+     * Maximum number of events in a block (events following a block header).
+     * Default is DEFAULT_BLOCK_COUNT_LIMIT.
+     */
+    private int blockCountMax;
+
+    /**
+     * Running count of the block number.
+     */
+    private int blockNumber = 0;
+
+    /**
+     * Size in bytes needed to write all the events in the <code>eventBufferHoldingList</code>
+     * to file or in the <code>eventHoldingList</code> to buffer.
+     */
+    private int holdingListTotalBytes;
+
+    /**
+     * Dictionary to include in xml format in the first event of the first block
+     * when writing the file.
+     */
+    private String xmlDictionary;
+
+    /**
+     * The next write will include an xml dictionary.
+     */
+    private boolean writeDictionary;
+
+    /**
+     * Bit information in the block headers:<p>
+     * <ul>
+     * <li>Bit one: is the first event a dictionary?  Used in first block only.
+     * <li>Bit two: is this the last block?
+     * </ul>
+     */
+    private BitSet bitInfo;
+
+    /**
+     * <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>.
+     */
+    private boolean toFile;
+
+    //-----------------------
+    // File related members
+    //-----------------------
+
+    /**
+     * The byte order in which to write a file.
+     */
+    private ByteOrder byteOrder;
+
+	/**
+	 * The output stream used for writing a file.
+	 */
+	private FileOutputStream fileOutputStream;
+
+    /**
+     * This is an internal byte buffer corresponding to one block (physical record). When this gets full it
+     * will be flushed to the external output stream (<code>dataOutputStream</code>) that represents the file.
+     */
+    private ByteBuffer blockBuffer;
+
+    /**
+     * List used to store events (in ByteBuffer form) temporarily before writing them to the file.
+     * This is done to accumulate events while keeping track of the total memory needed to write them.
+     * Once enough events are accumulated to at least fill a block, they are taken off this list,
+     * placed in a block, and written to file. This way the blocks contain an integral number of events.
+     */
+    private LinkedList<ByteBuffer> eventBufferHoldingList = new LinkedList<ByteBuffer>();
+
+    //-----------------------
+    // Buffer related members
+    //-----------------------
+
+    /**
+     * 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.
+     */
+    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.
+     * Once enough events are accumulated to at least fill a block, they are taken off this list
+     * and written to the buffer. This way the blocks contain an integral number of events.
+     */
+    private LinkedList<EvioBank> eventHoldingList = new LinkedList<EvioBank>();
+
+
+    
+
+    /**
+     * 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.
+     * @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);
+    }
+
+    /**
+     * 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 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(String filename) throws EvioException {
+        this(new File(filename), DEFAULT_BLOCK_SIZE_LIMIT, DEFAULT_BLOCK_COUNT_LIMIT,
+             ByteOrder.nativeOrder(), null, null);
+    }
+
+    /**
+     * 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.
+     *
+     *
+     * @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.
+     * @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.
+     * @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 {
+
+        if (blockSizeMax < 100 || blockSizeMax > 1000000) {
+            throw new EvioException("Pick a block size >= 1K and <= 1M");
+        }
+
+        if (blockCountMax < 1 || blockCountMax > 1000) {
+            throw new EvioException("Pick a block count >= 1 and <= 1000");
+        }
+
+    	toFile = true;
+        this.blockSizeMax = blockSizeMax;
+        this.blockCountMax = blockCountMax;
+        this.byteOrder = byteOrder;
+        this.xmlDictionary = xmlDictionary;
+
+        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.
+     *
+     * @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 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.
+     * @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.
+     */
+    public EventWriter(File file, int blockSizeMax, int blockCountMax, ByteOrder byteOrder,
+                          String xmlDictionary, BitSet bitInfo, boolean okToDelete) throws EvioException {
+
+        if (blockSizeMax < 100 || blockSizeMax > 1000000) {    // TODO: restore limits
+            throw new EvioException("Pick a block size >= 1K and <= 1M");
+        }
+
+        if (blockCountMax < 1 || blockCountMax > 1000) {
+            throw new EvioException("Pick a block count >= 1 and <= 1000");
+        }
+
+        toFile = true;
+    	this.blockSizeMax = blockSizeMax;
+        this.blockCountMax = blockCountMax;
+        this.byteOrder = byteOrder;
+        this.xmlDictionary = xmlDictionary;
+
+        if (bitInfo != null) {
+            this.bitInfo = (BitSet)bitInfo.clone();
+        }
+        else {
+            this.bitInfo = new BitSet(24);
+        }
+
+        if (xmlDictionary != null) {
+            this.bitInfo.set(0,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());
+    		}
+    	}
+
+		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());
+		}
+    }
+
+    /**
+     * 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.
+     * @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");
+        }
+
+        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;
+
+        if (bitInfo != null) {
+            this.bitInfo = (BitSet)bitInfo.clone();
+        }
+        else {
+            this.bitInfo = new BitSet(24);
+        }
+        
+        if (xmlDictionary != null) {
+            this.bitInfo.set(0,true);
+        }
+    }
+
+    /**
+     * Set the buffer being written into (initially set in constructor).
+     * This method allows the user to avoid having to create a new EventWriter
+     * each time a bank needs to be written to a different buffer.
+     * This does nothing if writing to a file.<p>
+     * Do <b>not</b> use this method unless you know what you are doing.
+     *
+     * @param buf the buffer to write to.
+     * @throws EvioException if this object was not closed prior to resetting the buffer.
+     */
+    public void setBuffer(ByteBuffer buf) throws EvioException {
+        if (toFile) return;
+        if (!closed) {
+            throw new EvioException("close EventWriter before changing buffers");
+        }
+
+        buffer = buf;
+        blockNumber = 0;
+        bufferHeaderPosition = 0;
+        holdingListTotalBytes = 0;
+        eventHoldingList.clear();
+        firstWrite = true;
+        closed = false;
+    }
+
+    /**
+     * Get the buffer being written into. This was initially supplied by user in constructor.
+     * This returns null if writing to a file.
+     *
+     * @return buffer being written into; null if writing to file.
+     */
+    public ByteBuffer getBuffer() {
+        return buffer;
+    }
+
+    /**
+     * If writing to a file, flush events waiting to be written,
+     * close the underlying data output stream and with it the file.
+     * If writing to a buffer, flush events waiting to be written.
+     * If writing to a buffer, {@link #setBuffer(java.nio.ByteBuffer)}
+     * can be called after this method to reset and reopen this object.
+     *
+     * @throws IOException if error writing to file
+     * @throws EvioException if writing to buffer and not enough space
+     */
+    public void close() throws EvioException, IOException {
+        if (toFile) {
+            closeFile();
+        }
+        else {
+            closeBuffer();
+        }
+    }
+
+    /**
+     * Write an event (bank) to the file in evio version 4 format in blocks.
+     * Each block has an integral number of events. There are limits to the
+     * number of events in each block and the total size of each block.
+     * A dictionary passed to the constructor will be written as the very
+     * first event and does <b>not</b> need to be explicitly written using
+     * this method.
+     *
+     * @param bank the bank to write.
+     * @throws IOException error writing to file
+     * @throws EvioException if bank arg is null or not enough room in buffer
+     */
+    public void writeEvent(EvioBank bank) throws EvioException, IOException {
+        if (bank == null) {
+            throw new EvioException("Attempt to write null bank using EventWriter");
+        }
+
+        if (toFile) {
+            writeEventToFile(bank);
+        }
+        else {
+            writeEventToBuffer(bank);
+        }
+    }
+
+
+    /**
+     * Get a clean buffer. If none create one, else make sure the existing buffer is large enough,
+     * then initialize it. This is called when a physical record is filled and flushed to the file.
+     *
+     * @param size size in bytes of block
+     * @param eventCount number of events in block
+     */
+    private void getCleanBuffer(int size, int eventCount) {
+    	if (blockBuffer == null) {
+            // Allocate at least the max block size in order to minimize chances
+            // of having to allocate a bigger buffer later. Remember there is only
+            // one buffer that gets reused.
+            int initialSize = size < 4*blockSizeMax ? 4*blockSizeMax : size;
+    		blockBuffer = ByteBuffer.allocate(initialSize); // this buffer is backed by an array
+    		blockBuffer.order(byteOrder);
+    	}
+    	else {
+    		// make sure buffer is big enough (+ add extra 100K)
+            if (size > blockBuffer.capacity()) {
+                blockBuffer = ByteBuffer.allocate(size + 100000);
+                blockBuffer.order(byteOrder);
+            }
+            blockBuffer.clear();
+    	}
+
+    	// Write header words, some of which may be
+        // overwritten later when the values are determined.
+		blockBuffer.putInt(size/4);        // actual size of data being written in ints
+		blockBuffer.putInt(blockNumber++); // incremental count of blocks
+		blockBuffer.putInt(8);             // header size always 8
+		blockBuffer.putInt(eventCount);    // number of events in block
+		blockBuffer.putInt(0);             // unused
+		blockBuffer.putInt(BlockHeaderV4.generateSixthWord(bitInfo)); // version = 4 & bit info stored
+		blockBuffer.putInt(0);             // unused
+		blockBuffer.putInt(IBlockHeader.MAGIC_NUMBER); // MAGIC_NUMBER
+    }
+
+
+    /**
+     * Close the underlying data output stream, and with it the file.
+     *
+     * @throws IOException if error writing to file
+     */
+    synchronized private void closeFile() throws IOException {
+
+        int headerBytes = 32;
+        int requiredBufferByteSize = headerBytes;
+
+        // write any remaining events, if there are any to be written
+        if (eventBufferHoldingList.size() > 0) {
+
+            requiredBufferByteSize = holdingListTotalBytes + headerBytes;
+
+            if (writeDictionary) {
+                // don't include dictionary in event count
+                getCleanBuffer(requiredBufferByteSize, eventBufferHoldingList.size() - 1);
+                updateBitInfo(BlockHeaderV4.generateSixthWord(bitInfo, true, true));
+                // after we write the header & dictionary, reset dictionary bit
+                bitInfo.set(0, false);
+            }
+            else {
+                getCleanBuffer(requiredBufferByteSize, eventBufferHoldingList.size());
+                updateBitInfo(BlockHeaderV4.generateSixthWord(bitInfo, false, true));
+            }
+
+            writeDictionary = false;
+
+            // write events being stored
+            for (ByteBuffer buf : eventBufferHoldingList) {
+                blockBuffer.put(buf);
+            }
+        }
+        // if there are no remaining events ...
+        else {
+            // write an empty header with the "isEnd" bit set to true
+            getCleanBuffer(headerBytes, 0);
+            updateBitInfo(BlockHeaderV4.generateSixthWord(bitInfo, false, true));
+        }
+
+        // write header and possibly events to file
+        fileOutputStream.write(blockBuffer.array(), 0, requiredBufferByteSize);
+
+        // finish writing file
+        fileOutputStream.close();
+
+        closed = true;
+    }
+
+    /**
+     * Write an event (bank) to the file in evio version 4 format in blocks.
+     * Each block has an integral number of events. There are limits to the
+     * number of events in each block and the total size of each block.
+     * A dictionary passed to the constructor will be written as the very
+     * first event and does <b>not</b> need to be explicitly written using
+     * this method.
+     *
+     * @param bank the bank to write.
+     * @throws IOException error writing to file
+     */
+    synchronized private void writeEventToFile(EvioBank bank) throws IOException {
+
+        if (closed) {
+            throw new IOException("file was closed");
+        }
+
+        boolean gotDictionary = false;
+
+        // If first call to this method, do some 1-time initialization
+        if (blockBuffer == null) {
+            getCleanBuffer(0,0);
+            // If we've defined a dictionary, we'll make
+            // that the very first event we write.
+            if (xmlDictionary != null) {
+                gotDictionary = true;
+                writeDictionary = true;
+            }
+        }
+
+        // See how much space the event will take up and get it ready (turn into ByteBuffer).
+    	int currentEventBytes = bank.getTotalBytes();
+    	ByteBuffer currentEventBuffer = ByteBuffer.allocate(currentEventBytes);
+    	currentEventBuffer.order(byteOrder);
+    	bank.write(currentEventBuffer);
+        // reset to top of event buffer
+        currentEventBuffer.position(0);
+
+        //-----------------------------------------------------------------------------------
+        // Do we write to block buffer & file now or do we put the event on the holding list?
+        //
+        // The basic principle here is that if the block buffer has blockCountMax (200) or
+        // more events, it gets written. And if the block buffer would exceed is memory limit
+        // (4*blockSizeMax bytes) if another event were to be added, it gets written.
+        // The only condition in which a block would be bigger than this memory limit is if
+        // a single event itself is bigger than that limit.
+        //-----------------------------------------------------------------------------------
+
+        // header size in bytes
+        int headerBytes = 32;
+        // aim for this amount of data in each block (in bytes)
+        int roomForData = 4*blockSizeMax - headerBytes;
+
+        int requiredBufferBytes;
+
+        // If we're writing a dictionary, it must be the first event in the first block.
+        if (gotDictionary) {
+            // make a bank out of the dictionary
+            EvioBank dictBank = new EvioBank(0, DataType.CHARSTAR8, 0);
+            try {
+                dictBank.appendStringData(xmlDictionary);
+            }
+            catch (EvioException e) { /* never happen */ }
+
+            // put that into a buffer
+            int dictEventBytes = dictBank.getTotalBytes();
+            ByteBuffer dictEventBuffer = ByteBuffer.allocate(dictEventBytes);
+            dictEventBuffer.order(byteOrder);
+            dictBank.write(dictEventBuffer);
+            dictEventBuffer.position(0);
+
+            // add bank as first in list of events to be written
+            eventBufferHoldingList.addFirst(dictEventBuffer);
+            holdingListTotalBytes += dictEventBytes;
+        }
+
+
+        // If we have enough to overfill a block ...
+        if (eventBufferHoldingList.size() > 0 &&
+                (holdingListTotalBytes + currentEventBytes > roomForData)) {
+
+            // write out full block minus current event
+            requiredBufferBytes = holdingListTotalBytes + headerBytes;
+
+            if (writeDictionary) {
+                // don't include dictionary in event count
+                getCleanBuffer(requiredBufferBytes, eventBufferHoldingList.size() - 1);
+                // after we write the header & dictionary, reset dictionary bit
+                bitInfo.set(0, false);
+            }
+            else {
+                getCleanBuffer(requiredBufferBytes, eventBufferHoldingList.size());
+            }
+
+            // write events being stored to buffer
+            for (ByteBuffer buf : eventBufferHoldingList) {
+                blockBuffer.put(buf);
+            }
+
+            // write buffered events to file
+            fileOutputStream.write(blockBuffer.array(), 0, requiredBufferBytes);
+
+            // reset after writing done
+            writeDictionary = false;
+            eventBufferHoldingList.clear();
+            holdingListTotalBytes = 0;
+        }
+
+        // if current event is >= than the block limit ...
+        if (currentEventBytes >= roomForData) {
+            // write out only current event
+            requiredBufferBytes = currentEventBytes + headerBytes;
+            getCleanBuffer(requiredBufferBytes, 1);
+
+            blockBuffer.put(currentEventBuffer);
+        }
+        else {
+            // add this event to holding list
+            eventBufferHoldingList.add(currentEventBuffer);
+            holdingListTotalBytes += currentEventBytes;
+
+            // write out events only if we've reached the count limit
+            if (eventBufferHoldingList.size() < blockCountMax) {
+                return;
+            }
+
+            requiredBufferBytes = holdingListTotalBytes + headerBytes;
+
+            if (writeDictionary) {
+                // don't include dictionary in event count
+                getCleanBuffer(requiredBufferBytes, eventBufferHoldingList.size() - 1);
+                // after we write the header & dictionary, reset dictionary bit
+                bitInfo.set(0, false);
+            }
+            else {
+                getCleanBuffer(requiredBufferBytes, eventBufferHoldingList.size());
+            }
+
+            // write events being stored
+            for (ByteBuffer buf : eventBufferHoldingList) {
+                blockBuffer.put(buf);
+            }
+        }
+
+        // write events to file
+        fileOutputStream.write(blockBuffer.array(), 0, requiredBufferBytes);
+
+        // reset after writing done
+        writeDictionary = false;
+        eventBufferHoldingList.clear();
+        holdingListTotalBytes = 0;
+	}
+
+
+    /**
+     * Write a header into a given buffer.
+     *
+     * @param size size in bytes of block
+     * @param eventCount number of events in block
+     * @throws EvioException if not enough space in buffer
+     */
+    private void writeNewHeader(int size, int eventCount) throws EvioException {
+
+        // if no room left for a header to be written ...
+        if (buffer.remaining() < 32) {
+            throw new EvioException("No more buffer size exceeded");
+        }
+
+        // record where beginning of header is so we can go back and update some items
+        bufferHeaderPosition = buffer.position();
+
+    	// Write header words, some of which will be
+        // overwritten later when the values are determined.
+		buffer.putInt(size/4);        // actual size in ints
+		buffer.putInt(blockNumber++); // incremental count of blocks
+		buffer.putInt(8);             // header size always 8
+		buffer.putInt(eventCount);    // number of events in block
+		buffer.putInt(0);             // unused
+		buffer.putInt(BlockHeaderV4.generateSixthWord(bitInfo)); // version = 4 & bit info stored
+//System.out.println("writing 6th word = " + BlockHeaderV4.generateSixthWord(bitInfo));
+		buffer.putInt(0);             // unused
+		buffer.putInt(IBlockHeader.MAGIC_NUMBER); // MAGIC_NUMBER
+    }
+
+    /**
+     * Finish writing block headers and events to buffer.
+     *
+     * @throws EvioException if not enough space in buffer
+     */
+    synchronized private void closeBuffer() throws EvioException {
+
+        int headerBytes = 32;
+
+        // write any remaining events, if there are any to be written
+        if (eventHoldingList.size() > 0) {
+
+            int requiredBufferByteSize = holdingListTotalBytes + headerBytes;
+
+            if (writeDictionary) {
+                // don't include dictionary in event count
+                writeNewHeader(requiredBufferByteSize, eventBufferHoldingList.size() - 1);
+                updateBitInfoInBuffer(BlockHeaderV4.generateSixthWord(bitInfo, true, true));
+                // after we write the header & dictionary, reset dictionary bit
+                bitInfo.set(0, false);
+            }
+            else {
+                writeNewHeader(requiredBufferByteSize, eventBufferHoldingList.size());
+                updateBitInfoInBuffer(BlockHeaderV4.generateSixthWord(bitInfo, false, true));
+            }
+
+            writeDictionary = false;
+
+            // write events being stored to buffer
+            for (EvioBank bnk : eventHoldingList) {
+                bnk.write(buffer);
+            }
+        }
+        // if there are no remaining events ...
+        else {
+            // write an empty header with the "isEnd" bit set to true
+            writeNewHeader(headerBytes, 0);
+            updateBitInfoInBuffer(BlockHeaderV4.generateSixthWord(bitInfo, false, true));
+        }
+
+        closed = true;
+    }
+
+    /**
+     * Write an event (bank) to a buffer in evio version 4 format in blocks.
+     * Each block has an integral number of events. There are limits to the
+     * number of events in each block and the total size of each block.
+     * A dictionary passed to the constructor will be written as the very
+     * first event and does <b>not</b> need to be explicitly written using
+     * this method.
+     *
+     * @param bank the bank to write.
+     * @throws EvioException if not enough room in buffer or close() already called
+     */
+    synchronized private void writeEventToBuffer(EvioBank bank) throws EvioException {
+
+        if (closed) {
+            throw new EvioException("close() has already been called");
+        }
+
+        // If first call to this method, do some 1-time initialization
+        if (firstWrite) {
+            firstWrite = false;
+
+            // If we've defined a dictionary, we'll make
+            // that the very first event we write.
+            if (xmlDictionary != null) {
+                writeDictionary = true;
+                // Dictionary must be the first event in the first block.
+                // Make a bank out of the dictionary
+                EvioBank dictBank = new EvioBank(0, DataType.CHARSTAR8, 0);
+                try {
+                    dictBank.appendStringData(xmlDictionary);
+                }
+                catch (EvioException e) {/* never happen */ }
+                int dictEventBytes = dictBank.getTotalBytes();
+
+                // add bank as first in list of events to be written
+                eventHoldingList.addFirst(dictBank);
+                holdingListTotalBytes += dictEventBytes;
+            }
+        }
+
+        // See how much space the event will take up and get it ready (turn into ByteBuffer).
+        int currentEventBytes = bank.getTotalBytes();
+
+        //-----------------------------------------------------------------------------------
+        // Do we write to block & ev buffers now or do we put the event on the holding list?
+        //
+        // The basic principle here is that if the block buffer has blockCountMax (200) or
+        // more events, it gets written. And if the block buffer would exceed its memory limit
+        // (4*blockSizeMax bytes) if another event were to be added, it gets written.
+        // The only condition in which a block would be bigger than this memory limit is if
+        // a single event itself is bigger than that limit.
+        //-----------------------------------------------------------------------------------
+
+        // header size in bytes
+        int headerBytes = 32;
+
+        // aim for this amount of data in each block (in bytes)
+        int roomForData = 4*blockSizeMax - headerBytes;
+
+        int requiredBufferBytes;
+
+        // If we have enough to overfill a block ...
+        if (eventHoldingList.size() > 0 &&
+                (holdingListTotalBytes + currentEventBytes > roomForData)) {
+
+            // size of block without current event
+            requiredBufferBytes = holdingListTotalBytes + headerBytes;
+
+            if (writeDictionary) {
+                // don't include dictionary in event count
+                writeNewHeader(requiredBufferBytes, eventHoldingList.size() - 1);
+                // after we write the header & dictionary, reset dictionary bit
+                bitInfo.set(0, false);
+            }
+            else {
+                writeNewHeader(requiredBufferBytes, eventHoldingList.size());
+            }
+
+            // write events being stored to buffer
+            for (EvioBank bnk : eventHoldingList) {
+                bnk.write(buffer);
+            }
+
+            // reset after writing done
+            writeDictionary = false;
+            eventHoldingList.clear();
+            holdingListTotalBytes = 0;
+        }
+
+        // if current event is >= than the block limit ...
+        if (currentEventBytes >= roomForData) {
+            // write out only current event
+            requiredBufferBytes = currentEventBytes + headerBytes;
+            writeNewHeader(requiredBufferBytes, 1);
+            bank.write(buffer);
+        }
+        else {
+            // add this event to holding list
+            eventHoldingList.add(bank);
+            holdingListTotalBytes += currentEventBytes;
+
+            // write out events only if we've reached the count limit
+            if (eventHoldingList.size() < blockCountMax) {
+                return;
+            }
+
+            requiredBufferBytes = holdingListTotalBytes + headerBytes;
+
+            if (writeDictionary) {
+                // don't include dictionary in event count
+                writeNewHeader(requiredBufferBytes, eventHoldingList.size() - 1);
+                // after we write the header & dictionary, reset dictionary bit
+                bitInfo.set(0, false);
+            }
+            else {
+                writeNewHeader(requiredBufferBytes, eventHoldingList.size());
+            }
+
+            // write events being stored
+            for (EvioBank bnk : eventHoldingList) {
+                bnk.write(buffer);
+            }
+        }
+
+        // reset after writing done
+        writeDictionary = false;
+        eventHoldingList.clear();
+        holdingListTotalBytes = 0;
+	}
+
+
+
+    /**
+     * Update the bit information field in the header using an absolute put.
+     * @param word the 6th word value.
+     */
+    private void updateBitInfo(int word) {
+//System.out.println("Set bit info word to " + word);
+        blockBuffer.putInt(BIT_INFO_OFFSET, word);
+    }
+
+    /**
+     * Update the bit information field in the header using an absolute put.
+     * @param word the 6th word value.
+     */
+    private void updateBitInfoInBuffer(int word) {
+//System.out.println("Set bit info word to " + word);
+        buffer.putInt(bufferHeaderPosition + BIT_INFO_OFFSET, word);
+    }
+
+
+    /**
+     * Main program for testing.
+     *
+     * @param args ignored command line arguments.
+     */
+    public static void mainOrig(String args[]) {
+//      String infile  = "C:\\Documents and Settings\\heddle\\My Documents\\out.ev";
+//      String outfile = "C:\\Documents and Settings\\heddle\\My Documents\\out_copy.ev";
+        String infile  = "../../testdata/out.ev";
+        String outfile = "../../testdata/out_copy.ev";
+        int count = 0;
+
+        try {
+            //an EvioFile object is used for reading
+            EvioFile inFile = new EvioFile(new File(infile));
+            //an EvioWriter object is used for writing
+            EventWriter eventWriter = new EventWriter(new File(outfile));
+            //EventWriter eventWriter = new EventWriter(new File(outfile), 8192, true);
+
+            EvioEvent event;
+            while ((event = inFile.parseNextEvent()) != null) {
+//				System.out.println("EVENT LEN: " + event.getHeader().getLength());
+                eventWriter.writeEvent(event);
+                count++;
+            }
+
+            eventWriter.close();
+        }
+        catch (EvioException e) {
+            e.printStackTrace();
+        }
+        catch (IOException e) {
+            e.printStackTrace();
+        }
+        System.out.println("copied: " + count + " events.");
+
+        //compare the two files
+        EvioFile.compareEventFiles(new File(infile), new File(outfile));
+    }
+
+    /**
+     * Main program for testing.
+     *
+     * @param args ignored command line arguments.
+     */
+    public static void main(String args[]) {
+        String infile  = "/daqfs/home/timmer/coda/jevio-4.0/testdata/out.ev";
+        //String infile  = "/tmp/out_copy.ev";
+        String outfile = "/tmp/out_copy.ev";
+        int count = 0;
+
+        try {
+            EvioFile inFile = new EvioFile(new File(infile));
+
+            int eventCount = inFile.getEventCount();
+            System.out.println("eventCount = " + eventCount);
+
+            EvioEvent[] event = new EvioEvent[eventCount+1];
+            while ((event[count++] = inFile.parseNextEvent()) != null) {
+            }
+
+            long t1, t2, tTotal=0L;
+            for (int i=0; i < 40; i++) {
+                EventWriter eventWriter = new EventWriter(new File(outfile));
+                t1 = System.currentTimeMillis();
+                for (int j=0; j < eventCount; j++) {
+                    eventWriter.writeEvent(event[i]);
+                }
+                eventWriter.close();
[truncated at 1000 lines; 20 more skipped]

jevio-base/src/main/java/org/jlab/coda/jevio
EvioBank.java 1.2 -> 1.3
diff -N EvioBank.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ EvioBank.java	28 Feb 2012 19:41:36 -0000	1.3
@@ -0,0 +1,136 @@
+package org.jlab.coda.jevio;
+
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+
+/**
+ * This holds a CODA Bank structure. Mostly it has a header (a <code>BankHeader</code>) and the raw data stored as an
+ * byte array.
+ * 
+ * @author heddle
+ * 
+ */
+public class EvioBank extends BaseStructure {
+
+	/**
+	 * The XML record tag for a segment.
+	 */
+	public static final String ELEMENT_NAME = "bank";
+
+    /** For general convenience, allow an object to be attached to an evio bank. */
+    transient protected Object attachment;
+
+
+	/**
+	 * Null constructor for a bank.
+	 */
+	public EvioBank() {
+		super(new BankHeader());
+	}
+
+	/**
+	 * Constructor using a provided BankHeader
+	 * 
+	 * @param bankHeader the header to use.
+	 * @see BankHeader
+	 */
+	public EvioBank(BankHeader bankHeader) {
+		super(bankHeader);
+	}
+	
+	/**
+	 * This is the general constructor to use for a Bank.
+	 * @param tag the tag for the bank header.
+	 * @param dataType the (enum) data type for the content of the bank.
+	 * @param num sometimes, but not necessarily, an ordinal enumeration.
+	 */
+	public EvioBank(int tag, DataType dataType, int num) {
+		this(new BankHeader(tag, dataType, num));
+	}
+
+    /**
+     * Get the attached object.
+     * @return the attached object
+     */
+    public Object getAttachment() {
+        return attachment;
+    }
+
+    /**
+     * Set the attached object.
+     * @param attachment object to attach to this bank
+     */
+    public void setAttachment(Object attachment) {
+        this.attachment = attachment;
+    }
+
+	/**
+	 * This implements the abstract method from <code>BaseStructure</code>. It is a convenience method use instead of
+	 * "instanceof" to see what type of structure we have. Note: this returns the type of this structure, not the type
+	 * of data this structure holds.
+	 * 
+	 * @return the <code>StructureType</code> of this structure, which is a StructureType.BANK.
+	 * @see StructureType
+	 */
+	@Override
+	public StructureType getStructureType() {
+		return StructureType.BANK;
+	}
+	
+	/**
+	 * Write this bank structure out as an XML record.
+	 * @param xmlWriter the writer used to write the events.
+	 */
+	@Override
+	public void toXML(XMLStreamWriter xmlWriter) {
+
+		try {
+			commonXMLStart(xmlWriter);
+            if (header.dataType.isStructure()) {
+			    xmlWriter.writeAttribute("content", xmlContentAttributeName);
+            }
+			xmlWriter.writeAttribute("data_type", String.format("0x%x", header.dataType.getValue()));
+			xmlWriter.writeAttribute("tag", "" + header.tag);
+            xmlWriter.writeAttribute("num", "" + header.number);
+            xmlWriter.writeAttribute("length", "" + header.length);
+            xmlWriter.writeAttribute("ndata", "" + getNumberDataItems());
+            increaseXmlIndent();
+			commonXMLDataWrite(xmlWriter);
+            decreaseXmlIndent();
+			commonXMLClose(xmlWriter);
+		}
+		catch (XMLStreamException e) {
+			e.printStackTrace();
+		}
+	}
+
+	/**
+	 * Get the element name for the bank for writing to XML.
+	 * @return the element name for the structure for writing to XML.
+	 */
+	@Override
+	public String getXMLElementName() {
+		return ELEMENT_NAME;
+	}
+
+    /**
+     * Write myself out as a byte array of evio format data
+     * in the byte order given by {@link #getByteOrder}.
+     * This method is much more efficient than using {@link #write(java.nio.ByteBuffer)}.<p>
+     * <b>However, be warned that this method is only useful when a bank has
+     * just been read from a file or buffer. Once the user adds data or children
+     * to the bank, this method does NOT produce correct results.</b>
+     *
+     * @return byte array containing evio format data of this bank in currently set byte order
+     */
+    byte[] toArray() {
+        byte[] bArray = new byte[rawBytes.length + header.getHeaderLength()*4];
+        // write the header
+        header.toArray(bArray, 0, byteOrder);
+        // write the rest
+        System.arraycopy(rawBytes, 0, bArray, header.getHeaderLength()*4, rawBytes.length);
+        return bArray;
+    }
+
+
+}

jevio-base/src/main/java/org/jlab/coda/jevio
EvioDictionaryEntry.java 1.2 -> 1.3
diff -N EvioDictionaryEntry.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ EvioDictionaryEntry.java	28 Feb 2012 19:41:36 -0000	1.3
@@ -0,0 +1,244 @@
+package org.jlab.coda.jevio;
+
+import java.util.StringTokenizer;
+
+/**
+ * An entry into the evio dictionary.
+ * 
+ * @author heddle
+ * 
+ */
+public class EvioDictionaryEntry implements Comparable<EvioDictionaryEntry> {
+
+	/**
+	 * The tag will have to match the tag field in a segment.
+	 */
+	private int tag;
+
+	/**
+	 * The result of the number field being tokenized, with "." serving as the delimiter. These are tokens.
+	 */
+	private int numbers[];
+
+    /**
+	 * The nice name for this dictionary entry--the description.
+	 */
+	private String description;
+
+	/**
+	 * Constructor.
+	 * 
+	 * @param tag the string tag, will be converted into an int.
+	 * @param num the string num field, which might be <code>null</code>, and which will be tokenized (for possible
+	 *            ancestor matching) and converted into an array of ints.
+	 * @param description the nice name for this dictionary entry--the description.
+	 */
+	public EvioDictionaryEntry(String tag, String num, String description) {
+		this.description = description;
+
+		// the tag field should clean--no hierarchy
+		this.tag = Integer.parseInt(tag);
+
+		// The <code>num</code> value is a string, rather than an int, because it might represent
+		// a bank hierarchy. For example, a number of "2.3.1" means that a bank matches this
+		// entry if a) its num field in the header is 1, and b) its parent's num field is 3, and
+        // c) it's grandparent's num field is 2.
+		if (num != null) {
+			String tokens[] = tokens(num, ".");
+
+			numbers = new int[tokens.length];
+			// flip the order so numbers[0] is primary match.
+			int j = 0;
+			for (int i = tokens.length - 1; i >= 0; i--) {
+				numbers[j] = Integer.parseInt(tokens[i]);
+				j++;
+			}
+		}
+	}
+
+    /**
+     * Checks if a structure matches this dictionary entry. This is more complicated than at first glance, because a
+     * matching of not just the structure but also (for banks) the num fields of its immediate ancestors might be
+     * required.
+     *
+     * @param structure the structure to test.
+     * @return <code>true</code> if the structure matches.
+     */
+    public boolean match(BaseStructure structure) {
+
+        if (structure instanceof EvioEvent) {
+            if ((numbers == null) || (numbers.length != 1)) {
+                return false;
+            }
+            BaseStructureHeader header = structure.header;
+            return (tag == header.tag) && (numbers[0] == header.number);
+        }
+        else if (structure instanceof EvioBank) {
+            if (structure.header.tag != tag) {
+                return false;
+            }
+
+            if (numbers == null) {
+                return false;
+            }
+
+            for (int i = 0; i < numbers.length; i++) {
+                BaseStructureHeader header = structure.header;
+                if (numbers[i] != header.number) {
+                    return false;
+                }
+
+                structure = structure.getParent();
+                if (structure == null) {
+                    // Once we've reached the top event,
+                    // parent = null, but match is good. timmer 9/30/2010
+                    if (i >= numbers.length-1) {
+                        return true;
+                    }
+                    return false;
+                }
+                if (structure instanceof EvioSegment) {
+                    return false;
+                }
+                if (structure instanceof EvioTagSegment) {
+                    return false;
+                }
+            }
+            return true;
+        }
+        else {
+            BaseStructureHeader header = structure.header;
+            return (tag == header.tag);
+        }
+    }
+
+    /**
+     * We sort so that the entries with the most number of ancestors match.
+     *
+     * @param entry the object to compare against.
+     */
+    @Override
+	public int compareTo(EvioDictionaryEntry entry) {
+        if (entry.numbers == null) return -1;
+        if (numbers == null) return 1;
+
+        // we want the entrys with the longer ancestor trail up top
+		if (numbers.length > entry.numbers.length) {
+			return -1;
+		}
+		if (numbers.length < entry.numbers.length) {
+			return 1;
+		}
+		// same number of tokens
+		for (int i = 0; i < numbers.length; i++) {
+			if (numbers[i] < entry.numbers[i]) {
+				return -1;
+			}
+			if (numbers[i] > entry.numbers[i]) {
+				return 1;
+			}
+		}
+
+		return 0;
+	}
+
+	/**
+	 * This method breaks a string into an array of tokens.
+	 * 
+	 * @param str the string to decompose.
+	 * @param token the token character.
+	 * @return an array of tokens.
+	 */
+	private String[] tokens(String str, String token) {
+		StringTokenizer t = new StringTokenizer(str, token);
+		int num = t.countTokens();
+		String tokens[] = new String[num];
+
+		for (int i = 0; i < num; i++) {
+			tokens[i] = t.nextToken();
+		}
+
+		return tokens;
+	}
+
+	/**
+	 * Get the nice, hopefully descriptive name for this entry.
+	 * 
+	 * @return the nice, hopefully descriptive name for this entry.
+	 */
+	public String getDescription() {
+		return description;
+	}
+
+    /**
+     * Get the tag for this entry.
+     *
+     * @return the tag for this entry.
+     */
+    public int getTag() {
+        return tag;
+    }
+
+    /**
+     * Get an xml representation of this entry.
+     * @return an xml representation of this entry.
+     */
+    public StringBuilder toXML() {
+        StringBuilder sb = new StringBuilder(100);
+        sb.append("<xmldumpDictEntry name=\"");
+        sb.append(description);
+        sb.append("\"  tag=\"");
+        sb.append(tag);
+        sb.append("\"");
+    
+        String ns = null;
+        if (numbers != null) {
+            for (int i = numbers.length - 1; i >= 0; i--) {
+                if (ns == null) {
+                    ns = "";
+                }
+                ns += numbers[i];
+                if (i != 0) {
+                    ns += ".";
+                }
+            }
+        }
+
+        if (ns != null) {
+            sb.append("  num=\"");
+            sb.append(ns);
+            sb.append("\"");
+        }
+
+        sb.append("/>\n");
+        return sb;
+    }
+
+	/**
+	 * Get a string representation of this entry.
+	 * 
+	 * @return a string representation of this entry.
+	 */
+	@Override
+	public String toString() {
+		String ns = null;
+		if (numbers != null) {
+			for (int i = numbers.length - 1; i >= 0; i--) {
+				if (ns == null) {
+					ns = "";
+				}
+				ns += numbers[i];
+				if (i != 0) {
+					ns += ".";
+				}
+			}
+		}
+		if (ns == null) {
+			return String.format("tag: %-4d description: %s", tag, description);
+		}
+		else {
+			return String.format("tag: %-4d num: %-10s description: %s", tag, ns, description);
+		}
+	}
+
+}

jevio-base/src/main/java/org/jlab/coda/jevio
EvioEvent.java 1.2 -> 1.3
diff -N EvioEvent.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ EvioEvent.java	28 Feb 2012 19:41:36 -0000	1.3
@@ -0,0 +1,277 @@
+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;
+
+/**
+ * An event is really just the outer, primary bank. That is, the first structure in an event (aka logical record, aka
+ * buffer) must be a bank, probably a bank of banks.
+ * 
+ * The Event trivially extends bank, just so there can be a distinct <code>EvioEvent</code> class, for clarity.
+ * 
+ * @author heddle
+ * 
+ */
+public class EvioEvent extends EvioBank {
+		
+	/**
+	 * The XML record tag for an event.
+	 */
+	public static final String ELEMENT_NAME = "event";
+
+	/**
+	 * As the event is parsed, it is parsed into a tree.
+	 */
+	protected DefaultTreeModel treeModel;
+	
+	/**
+	 * This is not the "num" field from the header, but rather a number [1..] indicating
+	 * which event this was in the event file from which it was read. 
+	 */
+	private int eventNumber;
+
+    /**
+     * There may be a dictionary in xml associated with this event. Or there may not.
+     */
+    private String dictionaryXML;
+
+
+    /**
+	 * Explicit null constructor for evio event.
+	 */
+	public EvioEvent() {
+	}
+	
+    /**
+     * Constructor using a provided BankHeader.
+     *
+     * @param bankHeader the header to use.
+     * @see BankHeader
+     */
+    public EvioEvent(BankHeader bankHeader) {
+        super(bankHeader);
+    }
+
+	/**
+	 * This is a general constructor to use for an EvioEvent.
+     *
+	 * @param tag the tag for the event header (which is just a bank header).
+	 * @param dataType the (enum) data type for the content of the bank.
+	 * @param num sometimes, but not necessarily, an ordinal enumeration.
+	 */
+	public EvioEvent(int tag, DataType dataType, int num) {
+		this(new BankHeader(tag, dataType, num));
+	}
+
+
+    /**
+     * Is there an XML dictionary associated with this event?
+     * @return <code>true</code> if there is an XML dictionary associated with this event,
+     *         else <code>false</code>
+     */
+    public boolean hasDictionaryXML() {
+        return dictionaryXML != null;
+    }
+
+    /**
+     * Get the XML dictionary associated with this event if there is one.
+     * @return the XML dictionary associated with this event if there is one, else null
+     */
+    public String getDictionaryXML() {
+        return dictionaryXML;
+    }
+
+    /**
+     * Set the XML dictionary associated with this event.
+     * @param dictionaryXML the XML dictionary associated with this event.
+     */
+    public void setDictionaryXML(String dictionaryXML) {
+        this.dictionaryXML = dictionaryXML;
+    }
+
+	/**
+	 * Create a string representation of the event.
+	 * @return a string representation of the event.
+	 */
+	@Override
+	public  String toString() {
+		
+		String description = getDescription();
+		if (INameProvider.NO_NAME_STRING.equals(description)) {
+			description = null;
+		}
+		
+		String s = "<Event> " + " len (ints): " + header.length + " " + " tag: " + header.tag + " num: " + header.number;
+        if (header.dataType.isStructure()) {
+            s += " content: " + xmlContentAttributeName;
+        }
+		if (description != null) {
+			s += " [" + description + "]";
+		}
+		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;
+	}
+	
+	/**
+	 * 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.
+	 * @param child the child structure being added to the tree.
+	 * @param parent the parent structure of the new child.
+	 */
+	public void insert(BaseStructure child, BaseStructure parent) {
+		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.
+     * @return a number [1..] indicating which event this was in the event file from which
+     * it was read.
+     */
+    public int getEventNumber() {
+        return eventNumber;
+    }
+
+	/**
+	 * This sets 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.
+	 * @param eventNumber a number [1..] indicating which event this was in the event file from which
+	 * it was read.
+	 */
+	public void setEventNumber(int eventNumber) {
+		this.eventNumber = eventNumber;
+	}
+
+	/**
+	 * Write this event structure out as an XML record.
+	 * @param xmlWriter the writer used to write the events.
+	 */
+	@Override
+	public void toXML(XMLStreamWriter xmlWriter) {
+
+		try {
+			int totalLen = header.length + 1;
+            increaseXmlIndent();
+            xmlWriter.writeCharacters(xmlIndent);
+			xmlWriter.writeComment(" Buffer " + getEventNumber() + " contains " + totalLen + " words (" + 4*totalLen + " bytes)");
+			commonXMLStart(xmlWriter);
+			xmlWriter.writeAttribute("format", "evio");
+			xmlWriter.writeAttribute("count", "" + getEventNumber());
+            if (header.dataType.isStructure()) {
+			    xmlWriter.writeAttribute("content", xmlContentAttributeName);
+            }
+			xmlWriter.writeAttribute("data_type", String.format("0x%x", header.dataType.getValue()));
+			xmlWriter.writeAttribute("tag", "" + header.tag);
+			xmlWriter.writeAttribute("num", "" + header.number);
+            xmlWriter.writeAttribute("length", "" + header.length);
+            increaseXmlIndent();
+			commonXMLDataWrite(xmlWriter);
+            decreaseXmlIndent();
+			commonXMLClose(xmlWriter);
+            xmlWriter.writeCharacters("\n");
+            decreaseXmlIndent();
+		}
+		catch (XMLStreamException e) {
+			e.printStackTrace();
+		}
+	}
+
+	/**
+	 * Get the element name for the bank for writing to XML.
+	 * @return the element name for the structure for writing to XML.
+	 */
+	@Override
+	public String getXMLElementName() {
+		return ELEMENT_NAME;
+	}
+
+}

jevio-base/src/main/java/org/jlab/coda/jevio
EvioException.java 1.2 -> 1.3
diff -N EvioException.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ EvioException.java	28 Feb 2012 19:41:36 -0000	1.3
@@ -0,0 +1,43 @@
+package org.jlab.coda.jevio;
+
+/**
+ * This is a general exception used to indicate a problem in the Jevio package.
+ * 
+ * @author heddle
+ * 
+ */
+@SuppressWarnings("serial")
+public class EvioException extends Exception {
+
+    /**
+     * Create an EVIO Exception indicating an error specific to the EVIO system.
+     * {@inheritDoc}<p/>
+     *
+     * @param message {@inheritDoc}<p/>
+     */
+    public EvioException(String message) {
+        super(message);
+    }
+
+    /**
+     * Create an EVIO Exception with the specified message and cause.
+     * {@inheritDoc}<p/>
+     *
+     * @param message {@inheritDoc}<p/>
+     * @param cause   {@inheritDoc}<p/>
+     */
+    public EvioException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    /**
+     * Create an EVIO Exception with the specified cause.
+     * {@inheritDoc}<p/>
+     *
+     * @param cause {@inheritDoc}<p/>
+     */
+    public EvioException(Throwable cause) {
+        super(cause);
+    }
+
+}

jevio-base/src/main/java/org/jlab/coda/jevio
EvioFile.java 1.2 -> 1.3
diff -N EvioFile.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ EvioFile.java	28 Feb 2012 19:41:36 -0000	1.3
@@ -0,0 +1,937 @@
+package org.jlab.coda.jevio;
+
+import java.io.*;
+import java.nio.*;
+import java.nio.channels.FileChannel;
+import java.util.BitSet;
+import java.util.List;
+
+import javax.xml.stream.FactoryConfigurationError;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+
+/**
+ * This is the a class of interest to the user. Create an <code>EvioFile</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 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.
+ * It is up to the listener to decide what to do with the structures.
+ * <p>
+ * 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>EvioFile</code> is used for reading and parsing events only.
+ * To write an event file, use an <code>EventWriter</code> object.
+ *
+ * @author heddle
+ *
+ */
+public class EvioFile {
+
+    /**
+	 * This <code>enum</code> denotes the status of a read. <br>
+	 * SUCCESS indicates a successful read. <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>
+	 * UNKNOWN_ERROR indicates that an unrecoverable error has occurred.
+	 */
+	public static enum ReadStatus {
+		SUCCESS, END_OF_FILE, EVIO_EXCEPTION, UNKNOWN_ERROR
+	}
+
+	/**
+	 * This <code>enum</code> denotes the status of a write.<br>
+	 * SUCCESS indicates a successful write. <br>
+	 * CANNOT_OPEN_FILE indicates that we cannot write because the destination file cannot be opened.<br>
+	 * EVIO_EXCEPTION indicates that an EvioException was thrown during a write.<br>
+	 * UNKNOWN_ERROR indicates that an unrecoverable error has occurred.
+	 */
+	public static enum WriteStatus {
+		SUCCESS, CANNOT_OPEN_FILE, EVIO_EXCEPTION, UNKNOWN_ERROR
+	}
+
+    /**
+     * Offset to get magic number from start of file.
+     */
+    private static final int MAGIC_OFFSET = 28;
+
+    /**
+     * Offset to get version number from start of file.
+     */
+    private static final int VERSION_OFFSET = 20;
+
+    /**
+     * Mask to get version number from 6th int in block.
+     */
+    private static final int VERSION_MASK = 0xff;
+
+	/**
+	 * The buffer representing a map of the input file.
+	 */
+	private MappedByteBuffer mappedByteBuffer;
+
+    /**
+     * Endianness of the data, either {@link java.nio.ByteOrder#BIG_ENDIAN} or {@link java.nio.ByteOrder#LITTLE_ENDIAN}.
+     */
+    private ByteOrder byteOrder;
+
+	/**
+	 * The current block header for evio versions 1, 2 & 3.
+	 */
+    private BlockHeaderV2 blockHeader2 = new BlockHeaderV2();
+
+    /**
+     * The current block header for evio version 4.
+     */
+    private BlockHeaderV4 blockHeader4 = new BlockHeaderV4();
+
+    /**
+     * Reference to current block header, any version, through interface.
+     */
+    private IBlockHeader blockHeader;
+
+	/**
+	 * Root element tag for XML file
+	 */
+	private static final String ROOT_ELEMENT = "evio-data";
+
+    /**
+     * Version 4 files may have an xml format dictionary in the
+     * first event of the first block;
+     */
+    private String xmlDictionary;
+
+	/**
+	 * Used to assign a transient number [1..n] to events as they are
+	 */
+	private int eventNumber = 0;
+
+	/**
+	 * This is the number of events in the file. It is not computed unless asked for, and if asked for it is computed
+	 * and cached in this variable.
+	 */
+	private int eventCount = -1;
+
+    /**
+     * Evio version number (1-4). Obtain this by reading first block header.
+     */
+    private int evioVersion;
+
+    /**
+     * Absolute path of the underlying file.
+     */
+    private String path;
+
+    /**
+     * Parser object for this file/buffer.
+     */
+    private EventParser parser;
+
+
+	/**
+	 * Creates an event file for reading.
+	 *
+	 * @param file the file that contains EVIO events.
+	 * @throws IOException
+	 */
+	public EvioFile(File file) throws IOException {
+		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);
+        }
+
+        // reset buffer to beginning
+        rewind();
+        parser = new EventParser();
+    }
+
+	/**
+	 * Creates an event file.
+	 *
+	 * @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
+	 */
+	public EvioFile(String path) throws IOException {
+		this(new File(path));
+	}
+
+    /**
+     * Get the path to the file.
+     * @return path to the file
+     */
+    public String getPath() {
+        return path;
+    }
+
+    /**
+     * Get the file/buffer parser.
+     * @return file/buffer parser.
+     */
+    public EventParser getParser() {
+        return parser;
+    }
+
+    /**
+     * Set the file/buffer parser.
+     * @param parser file/buffer parser.
+     */
+    public void setParser(EventParser parser) {
+        if (parser != null) {
+            this.parser = parser;
+        }
+    }
+
+    /**
+     * Get the xml format dictionary is there is one.
+     *
+     * @return xml format dictionary, else null.
+     */
+    public String getXmlDictionary() {
+        return xmlDictionary;
+    }
+
+    /**
+     * Does this evio file have an associated dictionary?
+     *
+     * @return <code>true</code> if this evio file has an associated dictionary,
+     *         else <code>false</code>
+     */
+    public boolean hasDictionary() {
+        return xmlDictionary != null;
+    }
+
+    /**
+     * Get the number of events remaining in the file.
+     *
+     * @return number of events remaining in the file
+     * @throws org.jlab.coda.jevio.EvioException if failed reading from coda v3 file
+     */
+    public int getNumEventsRemaining() throws EvioException {
+        return getEventCount() - eventNumber;
+    }
+
+	/**
+	 * Maps the file into memory. The data are not actually loaded in memory-- subsequent reads will read
+	 * random-access-like from the file.
+	 *
+	 * @param inputChannel the input channel.
+     * @throws IOException if file cannot be opened
+	 */
+	private synchronized void mapFile(FileChannel inputChannel) throws IOException {
+		long sz = inputChannel.size();
+		mappedByteBuffer = inputChannel.map(FileChannel.MapMode.READ_ONLY, 0L, sz);
+	}
+
+	/**
+	 * Obtain the file size using the memory mapped buffer's capacity, which should be the same.
+	 *
+	 * @return the file size in bytes--actually the mapped memory size.
+	 */
+	public int fileSize() {
+		return mappedByteBuffer.capacity();
+	}
+
+	/**
+	 * Get the memory mapped buffer corresponding to the event file.
+	 *
+	 * @return the memory mapped buffer corresponding to the event file.
+	 */
+	public MappedByteBuffer getMappedByteBuffer() {
+		return mappedByteBuffer;
+	}
+
+    /**
+     * Reads the block (physical record) header. Assumes the mapped buffer is positioned
+     * at the start of the next block header (physical record.)<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() {
+        // have enough remaining?
+        if (mappedByteBuffer.remaining() < 32) {
+            mappedByteBuffer.clear();
+            return ReadStatus.END_OF_FILE;
+        }
+
+        try {
+            // Set the byte order to match the file's ordering.
+
+            // Check the magic number for endianness. This requires
+            // peeking ahead 7 ints or 24 bytes. Set the endianness
+            // once we figure out what it is (buffer defaults to big endian).
+            byteOrder = mappedByteBuffer.order();
+            int magicNumber = mappedByteBuffer.getInt(MAGIC_OFFSET);
+
+            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;
+                }
+                else {
+                    byteOrder = ByteOrder.BIG_ENDIAN;
+                }
+                mappedByteBuffer.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 = mappedByteBuffer.getInt(VERSION_OFFSET) & VERSION_MASK;
+
+            try {
+                if (version > 0 && version < 4) {
+                    // cache the starting position
+                    blockHeader2.setBufferStartingPosition(mappedByteBuffer.position());
+
+                    // read the header data.
+                    blockHeader2.setSize(mappedByteBuffer.getInt());
+                    blockHeader2.setNumber(mappedByteBuffer.getInt());
+                    blockHeader2.setHeaderLength(mappedByteBuffer.getInt());
+                    blockHeader2.setStart(mappedByteBuffer.getInt());
+                    blockHeader2.setEnd(mappedByteBuffer.getInt());
+                    // skip version
+                    mappedByteBuffer.getInt();
+                    blockHeader2.setVersion(version);
+                    blockHeader2.setReserved1(mappedByteBuffer.getInt());
+                    blockHeader2.setMagicNumber(mappedByteBuffer.getInt());
+                    blockHeader = blockHeader2;
+                }
+                else if (version >= 4) {
+                    // cache the starting position
+                    blockHeader4.setBufferStartingPosition(mappedByteBuffer.position());
+
+                    // read the header data.
+                    blockHeader4.setSize(mappedByteBuffer.getInt());
+                    blockHeader4.setNumber(mappedByteBuffer.getInt());
+                    blockHeader4.setHeaderLength(mappedByteBuffer.getInt());
+                    blockHeader4.setEventCount(mappedByteBuffer.getInt());
+                    // unused
+                    mappedByteBuffer.getInt();
+                    // use 6th word to set bit info
+                    blockHeader4.parseToBitInfo(mappedByteBuffer.getInt());
+                    blockHeader4.setVersion(version);
+                    // unused
+                    mappedByteBuffer.getInt();
+                    blockHeader4.setMagicNumber(mappedByteBuffer.getInt());
+                    blockHeader = blockHeader4;
+                }
+                else {
+                    // error
+System.err.println("ERROR unsupported evio version number in file");
+                    return ReadStatus.EVIO_EXCEPTION;
+                }
+
+                // each file is restricted to one version, so set it once
+                if (evioVersion < 1) {
+                    evioVersion = version;
+                }
+            }
+            catch (EvioException e) {
+                e.printStackTrace();
+                return ReadStatus.EVIO_EXCEPTION;
+            }
+        }
+        catch (BufferUnderflowException a) {
+System.err.println("ERROR endOfBuffer " + a);
+            mappedByteBuffer.clear();
+            return ReadStatus.UNKNOWN_ERROR;
+        }
+
+        return ReadStatus.SUCCESS;
+    }
+
+
+   /**
+     * 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 2 & 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
+     */
+    public synchronized EvioEvent nextEvent() throws EvioException {
+        EvioEvent event = new EvioEvent();
+        BaseStructureHeader header = event.getHeader();
+
+        // Are we at the top of the file?
+        boolean isFirstEvent = false;
+        if (mappedByteBuffer.position() == 0) {
+            ReadStatus status = nextBlockHeader();
+            if (status != ReadStatus.SUCCESS) {
+                throw new EvioException("Failed reading block header in nextEvent.");
+            }
+            isFirstEvent = true;
+        }
+
+        // How many bytes remain in this block?
+        // Must see if we have to deal with crossing physical record boundaries (< version 4).
+        int bytesRemaining = blockBytesRemaining();
+        if (bytesRemaining < 0) {
+            throw new EvioException("Number of block bytes remaining is negative.");
+        }
+
+        // Are we exactly at the end of the block (physical record)?
+        if (bytesRemaining == 0) {
+            ReadStatus status = nextBlockHeader();
+            if (status == ReadStatus.SUCCESS) {
+                return nextEvent();
+            }
+            else if (status == ReadStatus.END_OF_FILE) {
+                return null;
+            }
+            else {
+                throw new EvioException("Failed reading block header in nextEvent.");
+            }
+        }
+        // Or have we already read in the last event?
+        else if (blockHeader.getBufferEndingPosition() == mappedByteBuffer.position()) {
+            return null;
+        }
+
+        // Version     4: once here, we are assured the entire next event is in this block.
+        // Version 1,2,3: no matter what, we can get the length of the next event.
+        // A non positive length indicates eof;
+        int length = mappedByteBuffer.getInt();
+        if (length < 1) {
+            return null;
+        }
+
+        header.setLength(length);
+        bytesRemaining -= 4; // just read in 4 bytes
+
+        // Version 2: if we were unlucky, after reading the length there are no bytes remaining in this bank.
+        // Don't really need the "if (version < 4)" here except for clarity.
+        if (evioVersion < 4) {
+            if (bytesRemaining == 0) {
+                ReadStatus status = nextBlockHeader();
+                if (status == ReadStatus.END_OF_FILE) {
+                    return null;
+                }
+                else if (status != ReadStatus.SUCCESS) {
+                    throw new EvioException("Failed reading block header in nextEvent.");
+                }
+                bytesRemaining = blockBytesRemaining();
+            }
+        }
+
+        // now should be good to go, except data may cross block boundary
+        // in any case, should be able to read the rest of the header.
+        if (byteOrder == ByteOrder.BIG_ENDIAN) {
+            // interested in bit pattern, not negative numbers
+            header.setTag(ByteDataTransformer.shortBitsToInt(mappedByteBuffer.getShort()));
+            int dt = ByteDataTransformer.byteBitsToInt(mappedByteBuffer.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(mappedByteBuffer.get()));
+        }
+        else {
+            header.setNumber(ByteDataTransformer.byteBitsToInt(mappedByteBuffer.get()));
+            int dt = ByteDataTransformer.byteBitsToInt(mappedByteBuffer.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(mappedByteBuffer.getShort()));
+        }
+        bytesRemaining -= 4; // just read in 4 bytes
+
+        // get the raw data
+        int eventDataSizeInts = header.getLength() - 1;
+        int eventDataSizeBytes = 4 * eventDataSizeInts;
+
+        try {
+            byte bytes[] = new byte[eventDataSizeBytes];
+
+            int bytesToGo = eventDataSizeBytes;
+            int offset = 0;
+
+            // Don't really need the "if (version < 4)" here except for clarity.
+            if (evioVersion < 4) {
+                // be in while loop if have to cross block boundary[ies].
+                while (bytesToGo > bytesRemaining) {
+                    mappedByteBuffer.get(bytes, offset, bytesRemaining);
+
+                    ReadStatus status = nextBlockHeader();
+                    if (status == ReadStatus.END_OF_FILE) {
+                        return null;
+                    }
+                    else if (status != ReadStatus.SUCCESS) {
+                        throw new EvioException("Failed reading block header after crossing boundary in nextEvent.");
+                    }
+                    bytesToGo -= bytesRemaining;
+                    offset += bytesRemaining;
+                    bytesRemaining = blockBytesRemaining();
+                }
+            }
+
+            // last (perhaps only) read.
+            mappedByteBuffer.get(bytes, offset, bytesToGo);
+            event.setRawBytes(bytes);
+            event.setByteOrder(byteOrder); // add this to track endianness, timmer
+
+            // if this is the very first event, check to see if it's a dictionary
+            if (isFirstEvent && blockHeader.hasDictionary()) {
+                // if we've NOT already been through this before, extract the dictionary
+                if (xmlDictionary == null) {
+                    xmlDictionary = event.getStringData()[0];
+                }
+                // give the user the next event, not the dictionary
+                return nextEvent();
+            }
+            else {
+                event.setEventNumber(++eventNumber);
+                return event;
+            }
+        }
+        catch (OutOfMemoryError ome) {
+            System.err.println("Out Of Memory\n" +
+                    "eventDataSizeBytes = " + eventDataSizeBytes + "\n" +
+                            "bytes Remaining = " + bytesRemaining + "\n" +
+                            "event Count: " + eventCount);
+            return null;
+        }
+        catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+	/**
+	 * This is the workhorse method. It retrieves the next event from the file, and then parses is SAX-like. It will
+	 * drill down and uncover all structures (banks, segments, and tagsegments) and notify any interested listeners.
+	 *
+	 * @return the event that was parsed. On error it throws an EvioException. On end of file, it returns
+	 *         <code>null</code>.
+	 * @throws EvioException
+	 */
+	public synchronized EvioEvent parseNextEvent() throws EvioException {
+		EvioEvent event = nextEvent();
+		if (event != null) {
+			parseEvent(event);
+		}
+		return event;
+	}
+
+	/**
+	 * This will parse an event, SAX-like. It will drill down and uncover all structures (banks, segments, and
+	 * tagsegments) and notify any interested listeners.
+	 *
+	 * 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 .
+	 *
+	 * @param evioEvent the event to parse.
+	 * @throws EvioException
+	 */
+	public synchronized void parseEvent(EvioEvent evioEvent) throws EvioException {
+		parser.parseEvent(evioEvent);
+	}
+
+	/**
+	 * Get the number of bytes remaining in the current block (physical record). This is used for pathology checks like
+	 * crossing the block boundary.
+	 *
+	 * @return the number of bytes remaining in the current block (physical record).
+	 */
+	private int blockBytesRemaining() {
+		try {
+			return blockHeader.bytesRemaining(mappedByteBuffer.position());
+		}
+		catch (EvioException e) {
+			e.printStackTrace();
+			return -1;
+		}
+	}
+
+	/**
+	 * The equivalent of rewinding the file. What it actually does is set the position of the mapped memory buffer back
+	 * to 0. This method, along with the two <code>position</code> and the <code>close</code> method, allows
+	 * applications to treat this in a normal random access file manner.
+	 */
+	public void rewind() {
+		mappedByteBuffer.position(0);
+		eventNumber = 0;
+	}
+
+	/**
+	 * This is equivalent to obtaining the current position in the file. What it actually does is return the position of
+	 * the mapped memory buffer. This method, along with the <code>rewind</code>, <code>position(int)</code> and the
+	 * <code>close</code> method, allows applications to treat this in a normal random access file manner.
+	 *
+	 * @return the position of the file.
+	 */
+	public int position() {
+		return mappedByteBuffer.position();
+	}
+
+	/**
+	 * This is equivalent to setting the current position in the file. What it actually does is set the position of the
+	 * mapped memory buffer. This method, along with the <code>rewind</code>, <code>position()</code> and the
+	 * <code>close</code> method, allows applications to treat this in a normal random access file manner.
+	 *
+	 * @param position the new position of the file.
+	 */
+	public void position(int position) {
+		mappedByteBuffer.position(position);
+	}
+
+	/**
+	 * This is equivalent to closing the file. What it actually does is clear all data from the mapped memory buffer,
+	 * and sets its position to 0. This method, along with the <code>rewind</code> and the two <code>position()</code>
+	 * methods, allows applications to treat this in a normal random access file manner.
+	 */
+	public void close() {
+		mappedByteBuffer.clear();
+	}
+
+	/**
+	 * This returns the current (active) block (physical record) header. Since most users have no interest in physical
+	 * records, this method should not be used often. Mostly it is used by the test programs in the
+	 * <code>EvioFileTest</code> class.
+	 *
+	 * @return the current block header.
+	 */
+	public IBlockHeader getCurrentBlockHeader() {
+		return blockHeader;
+	}
+
+	/**
+	 * Go to a specific event in the file. The events are numbered 1..N.
+     * This number is transient--it is not part of the event as stored in the evio file.
+	 *
+	 * @param evNumber the event number in a 1..N counting sense, from the start of the file.
+     * @return the specified event in the file.
+	 */
+	public EvioEvent gotoEventNumber(int evNumber) {
+
+		if (evNumber < 1) {
+			return null;
+		}
+
+		// rewind
+		rewind();
+		EvioEvent event;
+
+		try {
+			// get the first evNumber - 1 events without parsing
+			for (int i = 1; i < evNumber; i++) {
+				event = nextEvent();
+				if (event == null) {
+					throw new EvioException("Asked to go to event: " + evNumber + ", which is beyond the end of file");
+				}
+			}
+			// get one more event, the evNumber'th event, with parsing
+			return parseNextEvent();
+		}
+		catch (EvioException e) {
+			e.printStackTrace();
+		}
+		return null;
+	}
+
+	/**
+	 * Rewrite the entire file to XML.
+	 *
+	 * @param path the path to the XML file.
+	 * @return the status of the write.
+	 */
+	public WriteStatus toXMLFile(String path) {
+		return toXMLFile(path, null);
+	}
+
+	/**
+	 * Rewrite the file to XML (not including dictionary).
+	 *
+	 * @param path the path to the XML file.
+	 * @param progressListener and optional progress listener, can be <code>null</code>.
+	 * @return the status of the write.
+	 * @see org.jlab.coda.jevio.IEvioProgressListener
+	 */
+	public WriteStatus toXMLFile(String path, IEvioProgressListener progressListener) {
+		FileOutputStream fos;
+
+		try {
+			fos = new FileOutputStream(path);
+		}
+		catch (FileNotFoundException e) {
+			e.printStackTrace();
+			return WriteStatus.CANNOT_OPEN_FILE;
+		}
+
+		try {
+			XMLStreamWriter xmlWriter = XMLOutputFactory.newInstance().createXMLStreamWriter(fos);
+			xmlWriter.writeStartDocument();
+			xmlWriter.writeCharacters("\n");
+			xmlWriter.writeComment("Event source file: " + path);
+
+			// start the root element
+			xmlWriter.writeCharacters("\n");
+			xmlWriter.writeStartElement(ROOT_ELEMENT);
+			xmlWriter.writeAttribute("numevents", "" + getEventCount());
+
+			// now loop through the events
+			// rewind
+			rewind();
+			EvioEvent event;
+			try {
+				while ((event = parseNextEvent()) != null) {
+					event.toXML(xmlWriter);
+					// anybody interested in progress?
+					if (progressListener != null) {
+						progressListener.completed(event.getEventNumber(), getEventCount());
+					}
+				}
+			}
+			catch (EvioException e) {
+				e.printStackTrace();
+				return WriteStatus.UNKNOWN_ERROR;
+			}
+
+			// done. Close root element, end the document, and flush.
+			xmlWriter.writeCharacters("\n");
+			xmlWriter.writeEndElement();
+			xmlWriter.writeEndDocument();
+			xmlWriter.flush();
+			xmlWriter.close();
+
+			try {
+				fos.close();
+			}
+			catch (IOException e) {
+				e.printStackTrace();
+			}
+		}
+		catch (XMLStreamException e) {
+			e.printStackTrace();
+			return WriteStatus.UNKNOWN_ERROR;
+		}
+        catch (FactoryConfigurationError e) {
+            return WriteStatus.UNKNOWN_ERROR;
+        }
+        catch (EvioException e) {
+            return WriteStatus.EVIO_EXCEPTION;
+        }
+
+		rewind();
+		System.out.println("XML write was successful");
+		return WriteStatus.SUCCESS;
+	}
+
+    /**
+     * This is the number of events in the file. It is not computed unless asked for,
+     * and if asked for it is computed and cached. Any dictionary event is <b>not</b>
+     * included in the count.
+     *
+     * @return the number of events in the file.
+     * @throws EvioException
+     */
+    public int getEventCount() throws EvioException {
+System.out.println("\nCalling getEventCount()\n");
+        if (eventCount < 0) {
+            rewind();
+            eventCount = 0;
+
+            // In evio version 4+, each block stores the number of events in its header
+            // so there's no need to parse through all the events again.
+            if (evioVersion >= 4) {
+                ReadStatus status;
+                while (true) {
+                    status = nextBlockHeader();
+                    if (status == ReadStatus.END_OF_FILE) {
+                        break;
+                    }
+                    else if (status != ReadStatus.SUCCESS) {
+                        throw new EvioException("Failed reading block header while counting events.");
+                    }
+                    // note: getEventCount() does not include dictionary
+                    eventCount += blockHeader4.getEventCount();
+                    mappedByteBuffer.position(blockHeader4.nextBufferStartingPosition());
+                }
+            }
+            else {
+                while ((nextEvent()) != null) {
+                        eventCount++;
+                }
+            }
+
+            rewind();
+        }
+
+        return eventCount;
+    }
+
+	/**
+	 * Method used for diagnostics. It compares two event files. It checks the following (in order):<br>
+	 * <ol>
+	 * <li> That neither file is null.
+	 * <li> That both files exist.
+	 * <li> That neither file is a directory.
+	 * <li> That both files can be read.
+	 * <li> That both files are the same size.
+	 * <li> That both files contain the same number of events.
+	 * <li> Finally, that they are the same in a byte-by-byte comparison.
+	 * </ol>
+	 * NOTE: Two files with the same events but different physical record size will be reported as different. They will fail the same size test.
+	 * @param evFile1 first file to be compared
+     * @param evFile2 second file to be compared
+     * @return <code>true</code> if the files are, byte-by-byte, identical.
+	 */
+	public static boolean compareEventFiles(File evFile1, File evFile2) {
+
+		if ((evFile1 == null) || (evFile2 == null)) {
+			System.out.println("In compareEventFiles, one or both files are null.");
+			return false;
+		}
+
+		if (!evFile1.exists() || !evFile2.exists()) {
+			System.out.println("In compareEventFiles, one or both files do not exist.");
+			return false;
+		}
+
+		if (evFile1.isDirectory() || evFile2.isDirectory()) {
+			System.out.println("In compareEventFiles, one or both files is a directory.");
+			return false;
+		}
+
+		if (!evFile1.canRead() || !evFile2.canRead()) {
+			System.out.println("In compareEventFiles, one or both files cannot be read.");
+			return false;
+		}
+
+		String name1 = evFile1.getName();
+		String name2 = evFile2.getName();
+
+		long size1 = evFile1.length();
+		long size2 = evFile2.length();
+
+		if (size1 == size2) {
+			System.out.println(name1 + " and " + name2 + " have the same length: " + size1);
+		}
+		else {
+			System.out.println(name1 + " and " + name2 + " have the different lengths.");
+			System.out.println(name1 + ": " + size1);
+			System.out.println(name2 + ": " + size2);
+			return false;
+		}
+
+		try {
+			EvioFile evioFile1 = new EvioFile(evFile1);
+			EvioFile evioFile2 = new EvioFile(evFile2);
+			int evCount1 = evioFile1.getEventCount();
+			int evCount2 = evioFile2.getEventCount();
+			if (evCount1 == evCount2) {
+				System.out.println(name1 + " and " + name2 + " have the same #events: " + evCount1);
+			}
+			else {
+				System.out.println(name1 + " and " + name2 + " have the different #events.");
+				System.out.println(name1 + ": " + evCount1);
+				System.out.println(name2 + ": " + evCount2);
+				return false;
+			}
+		}
+        catch (EvioException e) {
+            e.printStackTrace();
+            return false;
+        }
+        catch (IOException e) {
+            e.printStackTrace();
+            return false;
+        }
+
+
+		System.out.print("Byte by byte comparison...");
+		System.out.flush();
+
+		int onetenth = (int)(1 + size1/10);
+
+		//now a byte-by-byte comparison
+		try {
+			FileInputStream fis1 = new FileInputStream(evFile1);
+			FileInputStream fis2 = new FileInputStream(evFile1);
+
+			for (int i = 0; i < size1; i++) {
+				try {
+					int byte1 = fis1.read();
+					int byte2 = fis2.read();
+
+					if (byte1 != byte2) {
+						System.out.println(name1 + " and " + name2 + " different at byte offset: " + i);
+						return false;
+					}
+
+					if ((i % onetenth) == 0) {
+						System.out.print(".");
+						System.out.flush();
+					}
+				}
+				catch (IOException e) {
+					e.printStackTrace();
+					return false;
+				}
+			}
+
+			System.out.println("");
+
+			try {
+				fis1.close();
+				fis2.close();
+			}
+			catch (IOException e) {
+				e.printStackTrace();
+			}
+		}
+		catch (FileNotFoundException e) {
+			e.printStackTrace();
+		}
+
+
+		System.out.println("files " + name1 + " and " + evFile2.getPath() + " are identical.");
+		return true;
+	}
+
+}

jevio-base/src/main/java/org/jlab/coda/jevio
EvioFileTest.java 1.2 -> 1.3
diff -N EvioFileTest.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ EvioFileTest.java	28 Feb 2012 19:41:36 -0000	1.3
@@ -0,0 +1,160 @@
+package org.jlab.coda.jevio;
+
+import org.jlab.coda.jevio.EvioFile.ReadStatus;
+
+/**
+ * A set of static functions that test evio files. It also has some other diagnostic methods for getting counts.
+ * 
+ * @author heddle
+ * 
+ */
+public class EvioFileTest {
+
+	/**
+	 * This enum is used for file testing. <br>
+	 * PASS indicates that a given test passed. <br>
+	 * FAIL indicates that a given test failed.
+	 */
+	public static enum TestResult {
+		PASS, FAIL
+	}
+
+	/**
+	 * Get a total count of the number of physical records.
+	 * 
+	 * @param evioFile the file to be processed.
+	 * @return the total count of blocks (physical records.)
+	 */
+	public static int totalBlockCount(EvioFile evioFile) {
+		evioFile.rewind();
+		ReadStatus status = ReadStatus.SUCCESS;
+		int count = 0;
+
+		while (status == ReadStatus.SUCCESS) {
+			status = evioFile.nextBlockHeader();
+			if (status == ReadStatus.SUCCESS) {
+				evioFile.position(evioFile.getCurrentBlockHeader().nextBufferStartingPosition());
+				count++;
+			}
+		}
+
+		System.out.println("total block count: " + count);
+
+		evioFile.rewind();
+		return count;
+	} // totalBlockCount
+
+	/**
+	 * Tests whether we can look through the file and find all the block headers.
+	 * 
+	 * @param evioFile the file to be tested.
+	 * @return the result of this test, either PASS or FAIL.
+	 */
+	public static TestResult readAllBlockHeadersTest(EvioFile evioFile) {
+		evioFile.rewind();
+		ReadStatus status = ReadStatus.SUCCESS;
+		int blockCount = 0;
+
+		while (status == ReadStatus.SUCCESS) {
+			status = evioFile.nextBlockHeader();
+			if (status == ReadStatus.SUCCESS) {
+				evioFile.position(evioFile.getCurrentBlockHeader().nextBufferStartingPosition());
+				blockCount++;
+			}
+		}
+
+		TestResult result;
+
+		// should have read to the end of the file
+		if (status == ReadStatus.END_OF_FILE) {
+			System.out.println("Total blocks read: " + blockCount);
+			result = TestResult.PASS;
+		}
+		else {
+			result = TestResult.FAIL;
+		}
+
+		System.out.println("readAllBlockHeadersTest: " + result);
+
+		evioFile.rewind();
+		return result;
+	} // readAllBlockHeadersTest
+
+	/**
+	 * Tests whether we can look through the file read all the events.
+	 * 
+	 * @param evioFile the file to be tested.
+	 * @return the result of this test, either <code>TestResult.PASS</code> or <code>TestResult.FAIL</code>.
+	 */
+	public static TestResult readAllEventsTest(EvioFile evioFile) {
+		// store current file position
+		int oldPosition = evioFile.position();
+
+		evioFile.rewind();
+		EvioEvent event;
+		int count = 0;
+		TestResult result = TestResult.PASS;
+
+		try {
+			while ((event = evioFile.nextEvent()) != null) {
+				count++;
+				BaseStructureHeader header = event.getHeader();
+
+				System.out.println(count + ")  size: " + header.getLength() + " type: " + header.getDataTypeName()
+						+ " \"" + event.getDescription() + "\"");
+			}
+		}
+		catch (EvioException e) {
+			e.printStackTrace();
+			result = TestResult.FAIL;
+		}
+
+		System.out.println("readAllBlockHeadersTest: " + result);
+
+		// restore file position
+		evioFile.position(oldPosition);
+		return result;
+	} // readAllEventsTest
+
+	/**
+	 * Tests whether we can parse events from the file.
+	 * 
+	 * @param evioFile the file to be tested.
+	 * @param num the number to parse. Will try to parse this many (unless it runs out.) Use -1 to parse all events.
+	 *            Note: if <code>num</code> is greater than the number of events in the file, it doesn't constitute an
+	 *            error.
+	 * @return the result of this test, either <code>TestResult.PASS</code> or <code>TestResult.FAIL</code>.
+	 */
+	public static TestResult parseEventsTest(EvioFile evioFile, int num) {
+		// store current file position
+		int oldPosition = evioFile.position();
+
+		if (num < 0) {
+			num = Integer.MAX_VALUE;
+		}
+
+		evioFile.rewind();
+		EvioEvent event;
+		int count = 0;
+		TestResult result = TestResult.PASS;
+
+		try {
+			while ((count < num) && ((event = evioFile.nextEvent()) != null)) {
+				evioFile.parseEvent(event);
+				count++;
+			}
+		}
+		catch (EvioException e) {
+			e.printStackTrace();
+			result = TestResult.FAIL;
+		}
+
+		System.out.println("parseEventsTest parsed: " + count + " events");
+		System.out.println("parseEventsTest result: " + result);
+
+		// restore file position
+		evioFile.position(oldPosition);
+		return result;
+	} // readAllEventsTest
+
+}

jevio-base/src/main/java/org/jlab/coda/jevio
EvioReader.java added at 1.1
diff -N EvioReader.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ EvioReader.java	28 Feb 2012 19:41:36 -0000	1.1
@@ -0,0 +1,1094 @@
+package org.jlab.coda.jevio;
+
+import java.io.*;
+import java.nio.*;
+import java.nio.channels.FileChannel;
+
+import javax.xml.stream.FactoryConfigurationError;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+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
+ * 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>.
+ *
+ * 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.
+ * It is up to the listener to decide what to do with the structures.
+ * <p>
+ * 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
+ *
+ */
+public class EvioReader {
+
+    /**
+	 * This <code>enum</code> denotes the status of a read. <br>
+	 * SUCCESS indicates a successful read. <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>
+	 * UNKNOWN_ERROR indicates that an unrecoverable error has occurred.
+	 */
+	public static enum ReadStatus {
+		SUCCESS, END_OF_FILE, EVIO_EXCEPTION, UNKNOWN_ERROR
+	}
+
+	/**
+	 * This <code>enum</code> denotes the status of a write.<br>
+	 * SUCCESS indicates a successful write. <br>
+	 * CANNOT_OPEN_FILE indicates that we cannot write because the destination file cannot be opened.<br>
+	 * EVIO_EXCEPTION indicates that an EvioException was thrown during a write.<br>
+	 * UNKNOWN_ERROR indicates that an unrecoverable error has occurred.
+	 */
+	public static enum WriteStatus {
+		SUCCESS, CANNOT_OPEN_FILE, EVIO_EXCEPTION, UNKNOWN_ERROR
+	}
+
+    /**
+     * Offset to get magic number from start of file.
+     */
+    private static final int MAGIC_OFFSET = 28;
+
+    /**
+     * Offset to get version number from start of file.
+     */
+    private static final int VERSION_OFFSET = 20;
+
+    /**
+     * Mask to get version number from 6th int in block.
+     */
+    private static final int VERSION_MASK = 0xff;
+
+    /**
+     * Root element tag for XML file
+     */
+    private static final String ROOT_ELEMENT = "evio-data";
+
+
+
+    /**
+     * Used to assign a transient number [1..n] to events as they are being read.
+     */
+    private int eventNumber = 0;
+
+    /**
+     * This is the number of events in the file. It is not computed unless asked for,
+     * and if asked for it is computed and cached in this variable.
+     */
+    private int eventCount = -1;
+
+    /**
+     * Evio version number (1-4). Obtain this by reading first block header.
+     */
+    private int evioVersion;
+
+    /**
+     * Endianness of the data being read, either
+     * {@link java.nio.ByteOrder#BIG_ENDIAN} or
+     * {@link java.nio.ByteOrder#LITTLE_ENDIAN}.
+     */
+    private ByteOrder byteOrder;
+
+	/**
+	 * The current block header for evio versions 1-3.
+	 */
+    private BlockHeaderV2 blockHeader2 = new BlockHeaderV2();
+
+    /**
+     * The current block header for evio version 4.
+     */
+    private BlockHeaderV4 blockHeader4 = new BlockHeaderV4();
+
+    /**
+     * Reference to current block header, any version, through interface.
+     */
+    private IBlockHeader blockHeader;
+
+    /**
+     * Is this the last block in the file or buffer?
+     */
+    private boolean lastBlock;
+
+    /**
+     * Version 4 files may have an xml format dictionary in the
+     * first event of the first block.
+     */
+    private String dictionaryXML;
+
+    /**
+     * The buffer being read.
+     */
+    private ByteBuffer byteBuffer;
+
+    /**
+     * Parser object for this file/buffer.
+     */
+    private EventParser parser;
+
+    /**
+     * Initial position of buffer or mappedByteBuffer when reading a file.
+     */
+    private int initialPosition;
+
+    //------------------------
+    // File specific members
+    //------------------------
+
+    /**
+     * Absolute path of the underlying file.
+     */
+    private String path;
+
+    /**
+     * The buffer representing a map of the input file which is also
+     * accessed through {@link #byteBuffer}.
+     */
+    private MappedByteBuffer mappedByteBuffer;
+
+
+
+
+    private void printBuffer(ByteBuffer buf, int lenInInts) {
+        IntBuffer ibuf = buf.asIntBuffer();
+        for (int i=0; i < lenInInts; i++) {
+            System.out.println("  Buf(" + i + ") = " + Integer.toHexString(ibuf.get(i)));
+        }
+    }
+
+    /**
+     * 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.
+     * @see EventWriter
+     * @throws IOException if read failure
+     */
+    public EvioReader(String path) throws IOException {
+        this(new File(path));
+    }
+
+    /**
+     * Constructor for reading an event file.
+     *
+     * @param file the file that contains events.
+     * @throws IOException if read failure
+     */
+    public EvioReader(File file) throws IOException {
+        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);
+        }
+
+        // reset buffer to beginning
+        rewind();
+
+        parser = new EventParser();
+    }
+
+
+    /**
+     * 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
+     */
+    public EvioReader(ByteBuffer byteBuffer) throws IOException {
+        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);
+        }
+
+        // reset buffer to beginning
+        rewind();
+
+        parser = new EventParser();
+    }
+
+
+    /**
+      * Get the path to the file.
+      * @return path to the file
+      */
+     public String getPath() {
+         return path;
+     }
+
+    /**
+     * Get the file/buffer parser.
+     * @return file/buffer parser.
+     */
+    public EventParser getParser() {
+        return parser;
+    }
+
+    /**
+     * Set the file/buffer parser.
+     * @param parser file/buffer parser.
+     */
+    public void setParser(EventParser parser) {
+        if (parser != null) {
+            this.parser = parser;
+        }
+    }
+
+     /**
+     * Get the XML format dictionary is there is one.
+     *
+     * @return XML format dictionary, else null.
+     */
+    public String getDictionaryXML() {
+        return dictionaryXML;
+    }
+
+    /**
+     * Does this evio file have an associated XML dictionary?
+     *
+     * @return <code>true</code> if this evio file has an associated XML dictionary,
+     *         else <code>false</code>
+     */
+    public boolean hasDictionaryXML() {
+        return dictionaryXML != null;
+    }
+
+    /**
+     * Get the number of events remaining in the file.
+     *
+     * @return number of events remaining in the file
+     * @throws EvioException if failed reading from coda v3 file
+     */
+    public int getNumEventsRemaining() throws EvioException {
+        return getEventCount() - eventNumber;
+    }
+
+	/**
+	 * Maps the file into memory. The data are not actually loaded in memory-- subsequent reads will read
+	 * random-access-like from the file.
+	 *
+     * @param inputChannel the input channel.
+     * @throws IOException if file cannot be opened
+	 */
+	private synchronized void mapFile(FileChannel inputChannel) throws IOException {
+		long sz = inputChannel.size();
+		mappedByteBuffer = inputChannel.map(FileChannel.MapMode.READ_ONLY, 0L, sz);
+        byteBuffer = mappedByteBuffer;
+	}
+
+	/**
+	 * Obtain the file size using the memory mapped buffer's capacity, which should be the same.
+	 *
+	 * @return the file size in bytes--actually the mapped memory size.
+	 */
+	public int fileSize() {
+		return byteBuffer.capacity();
+	}
+
+    /**
+     * Get the memory mapped buffer corresponding to the event file.
+     *
+     * @return the memory mapped buffer corresponding to the event file.
+     */
+    public MappedByteBuffer getMappedByteBuffer() {
+        return mappedByteBuffer;
+    }
+
+    /**
+     * Get the byte buffer being read directly or corresponding to the event file.
+     *
+     * @return the byte buffer being read directly or corresponding to the event file.
+     */
+    public ByteBuffer getByteBuffer() {
+        return byteBuffer;
+    }
+
+    /**
+     * Reads the block (physical record) header. Assumes the mapped buffer is positioned
+     * at the start of the next block header (physical record.)<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 {
+//            System.out.println("Print header =\n");
+//            printBuffer(byteBuffer, 8);
+            // Set the byte order to match the 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
+                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
+
+            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
+                    blockHeader4.setBufferStartingPosition(byteBuffer.position());
+
+                    // 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.parseToBitInfo(byteBuffer.getInt());
+                    blockHeader4.setVersion(version);
+                    lastBlock = blockHeader4.getBitInfo(1);
+                    // unused
+                    byteBuffer.getInt();
+                    blockHeader4.setMagicNumber(byteBuffer.getInt());
+                    blockHeader = blockHeader4;
+
+//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 {
+                    // error
+System.err.println("ERROR unsupported evio version number in file or buffer");
+                    return ReadStatus.EVIO_EXCEPTION;
+                }
+
+                // each file is restricted to one version, so set it once
+                if (evioVersion < 1) {
+                    evioVersion = version;
+                }
+            }
+            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;
+    }
+
+
+    /**
+      * 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();
+         BaseStructureHeader header = event.getHeader();
+
+         // Are we at the top of the file?
+         boolean isFirstEvent = false;
+         if (byteBuffer.position() == 0) {
+             ReadStatus status = nextBlockHeader();
+             if (status != ReadStatus.SUCCESS) {
+                 throw new EvioException("Failed reading block header in nextEvent.");
+             }
+             isFirstEvent = true;
+//System.out.println("nextEvent: BLOCK HEADER :\n" + blockHeader.toString());
+         }
+
+         // How many bytes remain in this block?
+         // Must see if we have to deal with crossing physical record boundaries (< version 4).
+         int bytesRemaining = blockBytesRemaining();
+         if (bytesRemaining < 0) {
+             throw new EvioException("Number of block bytes remaining is negative.");
+         }
+
+         // Are we exactly at the end of the block (physical record)?
+         if (bytesRemaining == 0) {
+             ReadStatus status = nextBlockHeader();
+             if (status == ReadStatus.SUCCESS) {
+//System.out.println("nextEvent: BLOCK HEADER:\n" + blockHeader.toString());
+                 return nextEvent();
+             }
+             else if (status == ReadStatus.END_OF_FILE) {
+                 return null;
+             }
+             else {
+                 throw new EvioException("Failed reading block header in nextEvent.");
+             }
+         }
+         // Or have we already read in the last event?
+         else if (blockHeader.getBufferEndingPosition() == byteBuffer.position()) {
+             return null;
+         }
+
+         // Version   4: once here, we are assured the entire next event is in this block.
+         // Version 1-3: no matter what, we can get the length of the next event.
+         // A non positive length indicates eof;
+         int length = byteBuffer.getInt();
+         if (length < 1) {
+             return null;
+         }
+
+         header.setLength(length);
+         bytesRemaining -= 4; // just read in 4 bytes
+
+         // Versions 1-3: if we were unlucky, after reading the length
+         //               there are no bytes remaining in this bank.
+         // Don't really need the "if (version < 4)" here except for clarity.
+         if (evioVersion < 4) {
+             if (bytesRemaining == 0) {
+                 ReadStatus status = nextBlockHeader();
+                 if (status == ReadStatus.END_OF_FILE) {
+                     return null;
+                 }
+                 else if (status != ReadStatus.SUCCESS) {
+                     throw new EvioException("Failed reading block header in nextEvent.");
+                 }
+                 bytesRemaining = blockBytesRemaining();
+             }
+         }
+
+         // Now should be good to go, except data may cross block boundary.
+         // In any case, should be able to read the rest of the header.
+         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();
+//System.out.println("EvioReader BigEnd: set data type = " + Integer.toHexString(header.getDataType().getValue()));
+//System.out.println("EvioReader BigEnd: set padding   = " + header.getPadding());
+             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();
+//System.out.println("EvioReader LittleEnd: set data type = " + Integer.toHexString(header.getDataType().getValue()));
+//System.out.println("EvioReader LittleEnd: set padding   = " + header.getPadding());
+             header.setTag(ByteDataTransformer.shortBitsToInt(byteBuffer.getShort()));
+         }
+         bytesRemaining -= 4; // just read in 4 bytes
+
+         // get the raw data
+         int eventDataSizeInts = header.getLength() - 1;
+         int eventDataSizeBytes = 4 * eventDataSizeInts;
+
+         try {
+             byte bytes[] = new byte[eventDataSizeBytes];
+
+             int bytesToGo = eventDataSizeBytes;
+             int offset = 0;
+
+             // Don't really need the "if (version < 4)" here except for clarity.
+             if (evioVersion < 4) {
+                 // be in while loop if have to cross block boundary[ies].
+                 while (bytesToGo > bytesRemaining) {
+                     byteBuffer.get(bytes, offset, bytesRemaining);
+
+                     ReadStatus status = nextBlockHeader();
+                     if (status == ReadStatus.END_OF_FILE) {
+                         return null;
+                     }
+                     else if (status != ReadStatus.SUCCESS) {
+                         throw new EvioException("Failed reading block header after crossing boundary in nextEvent.");
+                     }
+//System.out.println("nextEvent: BLOCK HEADER:\n" + blockHeader.toString());
+                     bytesToGo -= bytesRemaining;
+                     offset += bytesRemaining;
+                     bytesRemaining = blockBytesRemaining();
+                 }
+             }
+
+             // last (perhaps only) read.
+             byteBuffer.get(bytes, offset, bytesToGo);
+             event.setRawBytes(bytes);
+             event.setByteOrder(byteOrder); // add this to track endianness, timmer
+
+             // if this is the very first event, check to see if it's a dictionary
+             if (isFirstEvent && blockHeader.hasDictionary()) {
+                 // if we've NOT already been through this before, extract the dictionary
+                 if (dictionaryXML == null) {
+                     dictionaryXML = event.getStringData()[0];
+                 }
+                 // give the user the next event, not the dictionary
+                 return nextEvent();
+             }
+             else {
+                 event.setEventNumber(++eventNumber);
+                 return event;
+             }
+         }
+         catch (OutOfMemoryError ome) {
+             System.err.println("Out Of Memory\n" +
+                     "eventDataSizeBytes = " + eventDataSizeBytes + "\n" +
+                             "bytes Remaining = " + bytesRemaining + "\n" +
+                             "event Count: " + eventCount);
+             return null;
+         }
+         catch (Exception e) {
+             e.printStackTrace();
+             return null;
+         }
+     }
+
+	/**
+	 * This is the workhorse method. It retrieves the next event from the file, and then parses is SAX-like. It will
+	 * drill down and uncover all structures (banks, segments, and tagsegments) and notify any interested listeners.
+	 *
+	 * @return the event that was parsed. On error it throws an EvioException. On end of file, it returns
+	 *         <code>null</code>.
+	 * @throws EvioException if read failure or bad format
+	 */
+	public synchronized EvioEvent parseNextEvent() throws EvioException {
+		EvioEvent event = nextEvent();
+		if (event != null) {
+			parseEvent(event);
+		}
+		return event;
+	}
+
+	/**
+	 * This will parse an event, SAX-like. It will drill down and uncover all structures (banks, segments, and
+	 * tagsegments) and notify any interested listeners.
+	 *
+	 * 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 .
+	 *
+	 * @param evioEvent the event to parse.
+	 * @throws EvioException if bad format
+	 */
+	public synchronized void parseEvent(EvioEvent evioEvent) throws EvioException {
+		parser.parseEvent(evioEvent);
+	}
+
+	/**
+	 * Get the number of bytes remaining in the current block (physical record). This is used for pathology checks like
+	 * crossing the block boundary.
+	 *
+	 * @return the number of bytes remaining in the current block (physical record).
+	 */
+	private int blockBytesRemaining() {
+		try {
+			return blockHeader.bytesRemaining(byteBuffer.position());
+		}
+		catch (EvioException e) {
+			e.printStackTrace();
+			return -1;
+		}
+	}
+
+	/**
+	 * The equivalent of rewinding the file. What it actually does
+     * is set the position of the buffer back to 0. This method,
+     * along with the two <code>position()</code> and the <code>close()</code>
+     * method, allows applications to treat files in a normal random
+     * access manner.
+	 */
+	public void rewind() {
+		byteBuffer.position(initialPosition);
+		eventNumber = 0;
+        lastBlock = false;
+	}
+
+	/**
+	 * This is equivalent to obtaining the current position in the file.
+     * What it actually does is return the position of the buffer. This
+     * method, along with the <code>rewind()</code>, <code>position(int)</code>
+     * and the <code>close()</code> method, allows applications to treat files
+     * in a normal random access manner.
+	 *
+	 * @return the position of the buffer.
+	 */
+	public int position() {
+		return byteBuffer.position();
+	}
+
+	/**
+	 * This is equivalent to setting the current position in the file.
+     * What it actually does is set the position of the buffer. This
+     * method, along with the <code>rewind()</code>, <code>position()</code>
+     * and the <code>close()</code> method, allows applications to treat files
+     * in a normal random access manner.
+	 *
+	 * @param position the new position of the buffer.
+	 */
+	public void position(int position) {
+		byteBuffer.position(position);
+	}
+
+	/**
+	 * This is equivalent to closing the file. What it actually does is
+     * clear all data from the buffer, and sets its position to 0.
+     * This method, along with the <code>rewind()</code> and the two
+     * <code>position()</code> methods, allows applications to treat files
+     * in a normal random access manner.
+	 */
+	public void close() {
+		byteBuffer.clear();
+	}
+
+	/**
+	 * This returns the current (active) block (physical record) header.
+     * Since most users have no interest in physical records, this method
+     * should not be used often. Mostly it is used by the test programs in the
+	 * <code>EvioReaderTest</code> class.
+	 *
+	 * @return the current block header.
+	 */
+	public IBlockHeader getCurrentBlockHeader() {
+		return blockHeader;
+	}
+
+	/**
+	 * Go to a specific event in the file. The events are numbered 1..N.
+     * This number is transient--it is not part of the event as stored in the evio file.
+	 *
+	 * @param evNumber the event number in a 1..N counting sense, from the start of the file.
+     * @return the specified event in file.
+	 */
+	public EvioEvent gotoEventNumber(int evNumber) {         // todo: rename to getNthEvent() ?
+
+		if (evNumber < 1) {
+			return null;
+		}
+
+		// rewind
+		rewind();
+		EvioEvent event;
+
+        int currentCount = 0;
+
+		try {
+            // In evio version 4+, each block stores the number of events in its header
+            // so we can hop through headers without looking inside to save some time.
+            if (evioVersion >= 4) {             // todo: test this part
+                ReadStatus status;
+                while (true) {
+                    status = nextBlockHeader();
+                    if (status == ReadStatus.END_OF_FILE) {
+                        break;
+                    }
+                    else if (status != ReadStatus.SUCCESS) {
+                        throw new EvioException("Failed reading block header while counting events.");
+                    }
+
+                    currentCount += blockHeader4.getEventCount();
+                    if (currentCount < evNumber) {
+                        byteBuffer.position(blockHeader4.nextBufferStartingPosition());
+                    }
+                }
+            }
+
+			// get the first evNumber - 1 events without parsing
+			for (int i = currentCount+1; i < evNumber; i++) {
+				event = nextEvent();
+				if (event == null) {
+					throw new EvioException("Asked to go to event: " + evNumber + ", which is beyond the end of file");
+				}
+			}
+			// get one more event, the evNumber'th event, with parsing
+			return parseNextEvent();
+		}
+		catch (EvioException e) {
+			e.printStackTrace();
+		}
+		return null;
+	}
+
+	/**
+     * Rewrite the file to XML (not including dictionary).
+	 *
+	 * @param path the path to the XML file.
+	 * @return the status of the write.
+	 */
+	public WriteStatus toXMLFile(String path) {
+		return toXMLFile(path, null);
+	}
+
+	/**
+	 * Rewrite the file to XML (not including dictionary).
+	 *
+	 * @param path the path to the XML file.
+	 * @param progressListener and optional progress listener, can be <code>null</code>.
+	 * @return the status of the write.
+	 * @see IEvioProgressListener
+	 */
+	public WriteStatus toXMLFile(String path, IEvioProgressListener progressListener) {
+		FileOutputStream fos;
+
+		try {
+			fos = new FileOutputStream(path);
+		}
+		catch (FileNotFoundException e) {
+			e.printStackTrace();
+			return WriteStatus.CANNOT_OPEN_FILE;
+		}
+
+		try {
+			XMLStreamWriter xmlWriter = XMLOutputFactory.newInstance().createXMLStreamWriter(fos);
+			xmlWriter.writeStartDocument();
+			xmlWriter.writeCharacters("\n");
+			xmlWriter.writeComment("Event source file: " + path);
+
+			// start the root element
+			xmlWriter.writeCharacters("\n");
+			xmlWriter.writeStartElement(ROOT_ELEMENT);
+			xmlWriter.writeAttribute("numevents", "" + getEventCount());
+            xmlWriter.writeCharacters("\n");
+        
+			// now loop through the events
+			// rewind
+			rewind();
+			EvioEvent event;
+			try {
+				while ((event = parseNextEvent()) != null) {
+					event.toXML(xmlWriter);
+					// anybody interested in progress?
+					if (progressListener != null) {
+						progressListener.completed(event.getEventNumber(), getEventCount());
+					}
+				}
+			}
+			catch (EvioException e) {
+				e.printStackTrace();
+				return WriteStatus.UNKNOWN_ERROR;
+			}
+
+			// done. Close root element, end the document, and flush.
+//			xmlWriter.writeCharacters("\n");
+			xmlWriter.writeEndElement();
+			xmlWriter.writeEndDocument();
+			xmlWriter.flush();
+			xmlWriter.close();
+
+			try {
+				fos.close();
+			}
+			catch (IOException e) {
+				e.printStackTrace();
+			}
+		}
+		catch (XMLStreamException e) {
+			e.printStackTrace();
+			return WriteStatus.UNKNOWN_ERROR;
+		}
+        catch (FactoryConfigurationError e) {
+            return WriteStatus.UNKNOWN_ERROR;
+        }
+        catch (EvioException e) {
+            return WriteStatus.EVIO_EXCEPTION;
+        }
+
+		// TODO something better than just rewind again?
+		rewind();
+		return WriteStatus.SUCCESS;
+	}
+
+    /**
+     * This is the number of events in the file. It is not computed unless asked for,
+     * and if asked for it is computed and cached. Any dictionary event is <b>not</b>
+     * included in the count.
+     *
+     * @return the number of events in the file.
+     * @throws EvioException if read failure
+     */
+    public int getEventCount()  throws EvioException {
+        if (eventCount < 0) {
+            rewind();
+            eventCount = 0;
+
+            // In evio version 4+, each block stores the number of events in its header
+            // so there's no need to parse through all the events again.
+            if (evioVersion >= 4) {
+                ReadStatus status;
+                while (true) {
+                    status = nextBlockHeader();
+                    if (status == ReadStatus.END_OF_FILE) {
+                        break;
+                    }
+                    else if (status != ReadStatus.SUCCESS) {
+                        throw new EvioException("Failed reading block header while counting events.");
+                    }
+                    // note: getEventCount() does not include dictionary
+                    eventCount += blockHeader4.getEventCount();
+                    byteBuffer.position(blockHeader4.nextBufferStartingPosition());
+                }
+            }
+            else {
+                while ((nextEvent()) != null) {
+                        eventCount++;
+                }
+            }
+
+            rewind();
+        }
+
+        return eventCount;
+    }
+
+	/**
+	 * Method used for diagnostics. It compares two event files. It checks the following (in order):<br>
+	 * <ol>
+	 * <li> That neither file is null.
+	 * <li> That both files exist.
+	 * <li> That neither file is a directory.
+	 * <li> That both files can be read.
+	 * <li> That both files are the same size.
+	 * <li> That both files contain the same number of events.
+	 * <li> Finally, that they are the same in a byte-by-byte comparison.
+	 * </ol>
+     * NOTE: Two files with the same events but different physical record size will be reported as different.
+     *       They will fail the same size test.
+     * @param evFile1 first file to be compared
+     * @param evFile2 second file to be compared
+	 * @return <code>true</code> if the files are, byte-by-byte, identical.
+	 */
+	public static boolean compareEventFiles(File evFile1, File evFile2) {
+
+		if ((evFile1 == null) || (evFile2 == null)) {
+			System.out.println("In compareEventFiles, one or both files are null.");
+			return false;
+		}
+
+		if (!evFile1.exists() || !evFile2.exists()) {
+			System.out.println("In compareEventFiles, one or both files do not exist.");
+			return false;
+		}
+
+		if (evFile1.isDirectory() || evFile2.isDirectory()) {
+			System.out.println("In compareEventFiles, one or both files is a directory.");
+			return false;
+		}
+
[truncated at 1000 lines; 99 more skipped]

jevio-base/src/main/java/org/jlab/coda/jevio
EvioSegment.java 1.2 -> 1.3
diff -N EvioSegment.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ EvioSegment.java	28 Feb 2012 19:41:36 -0000	1.3
@@ -0,0 +1,96 @@
+package org.jlab.coda.jevio;
+
+import javax.xml.stream.XMLStreamWriter;
+import javax.xml.stream.XMLStreamException;
+
+/**
+ * This holds a CODA Segment structure. Mostly it has a header (a <code>SegementHeader</code>) and the raw data stored as an
+ * byte array.
+ * 
+ * @author heddle
+ * 
+ */
+public class EvioSegment extends BaseStructure {
+
+	/**
+	 * The XML record tag for a segment.
+	 */
+	public static final String ELEMENT_NAME = "segment";
+
+	/**
+	 * Null constructor creates an empty SegmentHeader.
+	 * 
+	 * @see SegmentHeader
+	 */
+	public EvioSegment() {
+		this(new SegmentHeader());
+	}
+
+	/**
+	 * Constructor using a provided SegmentHeader
+	 * 
+	 * @param segmentHeader the header to use.
+	 * @see SegmentHeader
+	 */
+	public EvioSegment(SegmentHeader segmentHeader) {
+		super(segmentHeader);
+	}
+	
+	/**
+	 * This is the general constructor to use for a Segment.
+	 * @param tag the tag for the segment header.
+	 * @param dataType the (enum) data type for the content of the segment.
+	 */
+	public EvioSegment(int tag, DataType dataType) {
+		this(new SegmentHeader(tag, dataType));
+	}
+
+	/**
+	 * This implements the abstract method from <code>BaseStructure</code>. It is a convenience method use instead of
+	 * "instanceof" to see what type of structure we have. Note: this returns the type of this structure, not the type
+	 * of data this structure holds.
+	 * 
+	 * @return the <code>StructureType</code> of this structure, which is a StructureType.SEGMENT.
+	 * @see StructureType
+	 */
+	@Override
+	public StructureType getStructureType() {
+		return StructureType.SEGMENT;
+	}
+	
+	/**
+	 * Write this segment structure out as an XML record.
+	 * @param xmlWriter the writer used to write the events.
+	 */
+	@Override
+	public void toXML(XMLStreamWriter xmlWriter) {
+
+        try {
+            commonXMLStart(xmlWriter);
+            if (header.dataType.isStructure()) {
+                xmlWriter.writeAttribute("content", xmlContentAttributeName);
+            }
+            xmlWriter.writeAttribute("data_type", String.format("0x%x", header.dataType.getValue()));
+            xmlWriter.writeAttribute("tag", "" + header.tag);
+            xmlWriter.writeAttribute("length", "" + header.length);
+            xmlWriter.writeAttribute("ndata", "" + getNumberDataItems());
+            increaseXmlIndent();
+            commonXMLDataWrite(xmlWriter);
+            decreaseXmlIndent();
+            commonXMLClose(xmlWriter);
+        }
+        catch (XMLStreamException e) {
+            e.printStackTrace();
+        }
+	}
+	
+	/**
+	 * Get the element name for the bank for writing to XML.
+	 * @return the element name for the structure for writing to XML.
+	 */
+	@Override
+	public String getXMLElementName() {
+		return ELEMENT_NAME;
+	}
+
+}

jevio-base/src/main/java/org/jlab/coda/jevio
EvioTagSegment.java 1.2 -> 1.3
diff -N EvioTagSegment.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ EvioTagSegment.java	28 Feb 2012 19:41:36 -0000	1.3
@@ -0,0 +1,97 @@
+package org.jlab.coda.jevio;
+
+import javax.xml.stream.XMLStreamWriter;
+import javax.xml.stream.XMLStreamException;
+
+/**
+ * This holds a CODA TagSegment structure. Mostly it has a header (a <code>TagSegementHeader</code>) and the raw data stored as an
+ * byte array.
+ * 
+ * @author heddle
+ * 
+ */
+public class EvioTagSegment extends BaseStructure {
+
+	/**
+	 * The XML record tag for a tag segment.
+	 */
+	public static final String ELEMENT_NAME = "tagsegment";
+
+
+	/**
+	 * Null constructor creates an empty TagSegmentHeader.
+	 * 
+	 * @see TagSegmentHeader
+	 */
+	public EvioTagSegment() {
+		this(new TagSegmentHeader());
+	}
+
+	/**
+	 * Constructor using a provided TagSegmentHeader
+	 * 
+	 * @param tagSegmentHeader the header to use.
+	 * @see TagSegmentHeader
+	 */
+	public EvioTagSegment(TagSegmentHeader tagSegmentHeader) {
+		super(tagSegmentHeader);
+	}
+	
+	/**
+	 * This is the general constructor to use for a TagSegment.
+	 * @param tag the tag for the tag segment header.
+	 * @param dataType the (enum) data type for the content of the tag segment.
+	 */
+	public EvioTagSegment(int tag, DataType dataType) {
+		this(new TagSegmentHeader(tag, dataType));
+	}
+
+	/**
+	 * This implements the abstract method from <code>BaseStructure</code>. It is a convenience method use instead of
+	 * "instanceof" to see what type of structure we have. Note: this returns the type of this structure, not the type
+	 * of data this structure holds.
+	 * 
+	 * @return the <code>StructureType</code> of this structure, which is a StructureType.TAGSEGMENT.
+	 * @see StructureType
+	 */
+	@Override
+	public StructureType getStructureType() {
+		return StructureType.TAGSEGMENT;
+	}
+	
+	/**
+	 * Write this tag segment structure out as an XML record.
+	 * @param xmlWriter the writer used to write the events.
+	 */
+	@Override
+	public void toXML(XMLStreamWriter xmlWriter) {
+		
+        try {
+            commonXMLStart(xmlWriter);
+            if (header.dataType.isStructure()) {
+			    xmlWriter.writeAttribute("content", xmlContentAttributeName);
+            }
+            xmlWriter.writeAttribute("data_type", String.format("0x%x", header.dataType.getValue()));
+            xmlWriter.writeAttribute("tag", "" + header.tag);
+            xmlWriter.writeAttribute("length", "" + header.length);
+            xmlWriter.writeAttribute("ndata", "" + getNumberDataItems());
+            increaseXmlIndent();
+            commonXMLDataWrite(xmlWriter);
+            decreaseXmlIndent();
+            commonXMLClose(xmlWriter);
+        }
+        catch (XMLStreamException e) {
+            e.printStackTrace();
+        }
+	}
+
+	/**
+	 * Get the element name for the bank for writing to XML.
+	 * @return the element name for the structure for writing to XML.
+	 */
+	@Override
+	public String getXMLElementName() {
+		return ELEMENT_NAME;
+	}
+
+}

jevio-base/src/main/java/org/jlab/coda/jevio
EvioXMLDictionary.java 1.2 -> 1.3
diff -N EvioXMLDictionary.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ EvioXMLDictionary.java	28 Feb 2012 19:41:36 -0000	1.3
@@ -0,0 +1,240 @@
+package org.jlab.coda.jevio;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.ByteArrayInputStream;
+import java.util.Collections;
+import java.util.Vector;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+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.
+ * 
+ * @author heddle
+ * 
+ */
+@SuppressWarnings("serial")
+public class EvioXMLDictionary extends Vector<EvioDictionaryEntry> implements INameProvider {
+
+	/**
+	 * There is only one type of element. This is its strange name.
+	 */
+	private static String ENTRY = "xmldumpDictEntry";
+
+	/**
+	 * The "name" attribute name.
+	 */
+	private static String NAME = "name";
+
+	/**
+	 * The "tag" attribute name.
+	 */
+	private static String TAG = "tag";
+
+	/**
+	 * The "num" attribute name.
+	 */
+	private static String NUM = "num";
+
+    /**
+     * Create an EvioXMLDictionary. This is not a standard CODA dictionary,
+     * but a format developed for use by GEMC.
+     *
+     * @param file file containing xml.
+     */
+    public EvioXMLDictionary(File file) {
+        this(getDomObject(file));
+    }
+
+    /**
+     * Create an EvioXMLDictionary. This is not a standard CODA dictionary,
+     * but a format developed for use by GEMC.
+     *
+     * @param xmlString string containing xml.
+     */
+    public EvioXMLDictionary(String xmlString) {
+        this(getDomObject(xmlString));
+    }
+
+    /**
+	 * Create an EvioXMLDictionary. This is not a standard CODA dictionary,
+     * but a format developed for use by GEMC.
+	 * 
+	 * @param domDocument DOM object representing xml dictionary.
+	 */
+	public EvioXMLDictionary(Document domDocument) {
+		super(100);
+
+		// 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);
+
+	} // 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;
+	}
+
+	/**
+	 * Return a dom object corresponding to an xml file.
+	 * 
+	 * @param file the XML File object
+	 * @return the dom object (or <code>null</code>.)
+	 */
+	private static Document getDomObject(File file) {
+		// get the factory
+		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+		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();
+		}
+		catch (SAXException e) {
+			e.printStackTrace();
+		}
+		catch (IOException e) {
+			e.printStackTrace();
+		}
+
+		return dom;
+	}
+
+    /**
+     * Return a dom object corresponding to an xml string.
+     *
+     * @param xmlString XML string representing dictionary
+     * @return the dom object (or <code>null</code>.)
+     */
+    private static Document getDomObject(String xmlString) {
+        // get the factory
+        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+        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();
+        }
+        catch (SAXException e) {
+            e.printStackTrace();
+        }
+        catch (IOException e) {
+            e.printStackTrace();
+        }
+
+        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();
+    }
+
+    /**
+     * Get a string representation of the dictionary.
+     * @return a string representation of the dictionary.
+     */
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder(4096);
+        sb.append("-- Dictionary --\n");
+        for (EvioDictionaryEntry entry : this) {
+            sb.append(entry.toString());
+            sb.append("\n");
+        }
+        return sb.toString();
+    }
+}

jevio-base/src/main/java/org/jlab/coda/jevio
IBlockHeader.java added at 1.1
diff -N IBlockHeader.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ IBlockHeader.java	28 Feb 2012 19:41:36 -0000	1.1
@@ -0,0 +1,137 @@
+package org.jlab.coda.jevio;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Make a common interface for different versions of the BlockHeader.
+ *
+ * @author timmer
+ */
+public interface IBlockHeader {
+    /**
+     * The magic number, should be the value of <code>magicNumber</code>.
+     */
+    public static final int MAGIC_NUMBER = 0xc0da0100;
+
+    /**
+	 * Get the size of the block (physical record).
+	 *
+	 * @return the size of the block (physical record) in ints.
+	 */
+    int getSize();
+
+    /**
+	 * Get the block number for this block (physical record).
+     * In a file, this is usually sequential.
+     *
+     * @return the block number for this block (physical record).
+     */
+    int getNumber();
+
+    /**
+	 * Get the block header length, in ints. This should be 8.
+     *
+     * @return the block header length. This should be 8.
+     */
+    int getHeaderLength();
+
+    /**
+     * Get the evio version of the block (physical record) header.
+     *
+     * @return the evio version of the block (physical record) header.
+     */
+    int getVersion();
+
+    /**
+	 * Get the magic number the block (physical record) header which should be 0xc0da0100.
+     *
+     * @return the magic number in the block (physical record).
+     */
+    int getMagicNumber();
+
+    /**
+     * Get the position in the buffer (in bytes) of this block's last data word.<br>
+     *
+     * @return the position in the buffer (in bytes) of this block's last data word.
+     */
+    int getBufferEndingPosition();
+
+    /**
+     * Get the starting position in the buffer (in bytes) from which this header was read--if that happened.<br>
+     * This is not part of the block header proper. It is a position in a memory buffer of the start of the block
+     * (physical record). It is kept for convenience. It is up to the reader to set it.
+     *
+     * @return the starting position in the buffer (in bytes) from which this header was read--if that happened.
+     */
+    int getBufferStartingPosition();
+
+    /**
+	 * Set the starting position in the buffer (in bytes) from which this header was read--if that happened.<br>
+     * This is not part of the block header proper. It is a position in a memory buffer of the start of the block
+     * (physical record). It is kept for convenience. It is up to the reader to set it.
+     *
+     * @param bufferStartingPosition the starting position in the buffer from which this header was read--if that
+     *            happened.
+     */
+    void setBufferStartingPosition(int bufferStartingPosition);
+
+    /**
+	 * Determines where the start of the next block (physical record) header in some buffer is located (in bytes).
+     * This assumes the start position has been maintained by the object performing the buffer read.
+     *
+     * @return the start of the next block (physical record) header in some buffer is located (in bytes).
+     */
+    int nextBufferStartingPosition();
+
+    /**
+	 * Determines where the start of the first event (logical record) in this block (physical record) is located
+     * (in bytes). This assumes the start position has been maintained by the object performing the buffer read.
+     *
+     * @return where the start of the first event (logical record) in this block (physical record) is located
+     *         (in bytes). Returns 0 if start is 0, signaling that this entire physical record is part of a
+     *         logical record that spans at least three physical records.
+     */
+    int firstEventStartingPosition();
+
+    /**
+	 * 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.
+     *
+     * @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
+     */
+    int bytesRemaining(int position) throws EvioException;
+
+    /**
+     * 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>
+     */
+    public boolean hasDictionary();
+
+    /**
+     * Is this the last block in the file or being sent over the network?
+     *
+     * @return <code>true</code> if this is the last block in the file or being sent
+     *         over the network, else <code>false</code>
+     */
+    public boolean isLastBlock();
+
+    /**
+	 * Write myself out a byte buffer. This write is relative--i.e., it uses the current position of the buffer.
+     *
+     * @param byteBuffer the byteBuffer to write to.
+     * @return the number of bytes written, which for a BlockHeader is 32.
+     */
+    int write(ByteBuffer byteBuffer);
+
+    /**
+     * Obtain a string representation of the block (physical record) header.
+     *
+     * @return a string representation of the block (physical record) header.
+     */
+    @Override
+    public String toString();
+}

jevio-base/src/main/java/org/jlab/coda/jevio
IEvioFilter.java 1.2 -> 1.3
diff -N IEvioFilter.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ IEvioFilter.java	28 Feb 2012 19:41:36 -0000	1.3
@@ -0,0 +1,31 @@
+package org.jlab.coda.jevio;
+
+/**
+ * This interface allows applications to create filters so that they only recieve certain structures
+ * when events are being processed. Below is a filter that accepts any structure that has tag = 400.
+ * <pre>
+ * IEvioFilter myFilter = new IEvioFilter() {
+ *     public boolean accept(StructureType structureType, IEvioStructure structure) {
+ *         return (structure.getHeader().getTag() == 400);
+ *     }
+ * };
+ * EventParser.getInstance().setEvioFilter(myFilter);
+ * </pre>
+ * @author heddle
+ *
+ */
+public interface IEvioFilter {
+
+	/**
+     * Accept or reject the given structure.
+     *
+	 * @param structureType the structure type, a <code>StructureType</code> enum, of the structure
+	 *                      that was just found, e.g., <code>StructureType.BANK</code>.
+	 * @param structure the structure that was just found. From its header the tag, num, length,
+     *                  and data type are available. The application can filter based on those
+     *                  quantities or on the data itself.
+	 * @return <code>true</code> if the structure passes the filter and should be given to the listeners.
+	 * @see StructureType
+	 */
+	public boolean accept(StructureType structureType, IEvioStructure structure);
+}

jevio-base/src/main/java/org/jlab/coda/jevio
IEvioListener.java 1.2 -> 1.3
diff -N IEvioListener.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ IEvioListener.java	28 Feb 2012 19:41:36 -0000	1.3
@@ -0,0 +1,35 @@
+package org.jlab.coda.jevio;
+
+import java.util.EventListener;
+
+/**
+ * In SAX like behavior, implementors will listen for structures encountered when an event is parsed.
+ * 
+ * @author heddle
+ * 
+ */
+public interface IEvioListener extends EventListener {
+
+	/**
+	 * Called when a structure is read while parsing an event.
+	 * 
+	 * NOTE: the user should NOT modify the event or the structure.
+	 * 
+	 * @param evioEvent the data type, BANK, SEGMENT, or TAGSEGMENT
+	 * @param structure the full structure, including header
+	 */
+	public void gotStructure(EvioEvent evioEvent, IEvioStructure structure);
+    
+    /**
+     * Starting to parse a new event.
+     * @param evioEvent the event in question.
+     */
+    public void startEventParse(EvioEvent evioEvent);
+
+    /**
+     * Done parsing a new event.
+     * @param evioEvent the event in question.
+     */
+    public void endEventParse(EvioEvent evioEvent);
+
+}

jevio-base/src/main/java/org/jlab/coda/jevio
IEvioProgressListener.java 1.2 -> 1.3
diff -N IEvioProgressListener.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ IEvioProgressListener.java	28 Feb 2012 19:41:36 -0000	1.3
@@ -0,0 +1,11 @@
+package org.jlab.coda.jevio;
+
+public interface IEvioProgressListener {
+
+	/**
+	 * Something is listenening for progress, e.g. the number of events that has been written to XML.
+	 * @param num the current number,
+	 * @param total the total number, i.e, we have completed num out of total.
+	 */
+	public void completed(int num, int total);
+}

jevio-base/src/main/java/org/jlab/coda/jevio
IEvioStructure.java 1.2 -> 1.3
diff -N IEvioStructure.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ IEvioStructure.java	28 Feb 2012 19:41:36 -0000	1.3
@@ -0,0 +1,117 @@
+package org.jlab.coda.jevio;
+
+/**
+ * This interface is implemented by classes representing the basic evio structures: banks, segments, and tagsegments.
+ * Note there is not "getNum" methods, because not all structures have num fields in their header, only BANKs do.
+ * 
+ * @author heddle
+ * 
+ */
+public interface IEvioStructure {
+
+	/**
+	 * Returns the header for this structure
+	 * 
+	 * @return the <code>BaseStructureHeader</code> for this structure.
+	 */
+	public BaseStructureHeader getHeader();
+
+	/**
+	 * Return the StructureType for this structure.
+	 * 
+	 * @return the StructureType for this structure.
+	 * @see StructureType
+	 */
+	public StructureType getStructureType();
+	
+	/**
+	 * Gets the raw data as an integer array, if the type as indicated by the
+	 * header is appropriate.
+	 * NOTE: since Java does not have unsigned primitives, both INT32 and UINT32 data types will be
+	 * returned as int arrays. The application will have to deal with reinterpreting signed ints that are
+	 * negative as unsigned ints
+	 * @return the data as an int array, or <code>null</code> if this makes no sense for the given type.
+	 */
+	public int[] getIntData();
+	
+	/**
+	 * Gets the raw data as a double array, if the type as indicated by the
+	 * header is appropriate.
+	 * @return the data as an double array, or <code>null</code> if this makes no sense for the given type.
+	 */
+	public double[] getDoubleData();
+	
+	/**
+	 * Gets the raw data as an byte array, if the type as indicated by the
+	 * header is appropriate.
+	 * NOTE: since Java does not have unsigned primitives, CHAR8 and UCHAR8 data types will be
+	 * returned as byte arrays. The application will have to deal with reinterpreting bytes as characters,
+	 * if necessary.
+	 * @return the data as an byte array, or <code>null</code> if this makes no sense for the given type.
+	 */
+	public byte[] getByteData();
+	
+    /**
+     * Gets the raw data as an array of String objects, if the type as indicated by the
+     * header is appropriate.
+     * @return the data as an array of String objects, 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 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.
+	 * NOTE: since Java does not have unsigned primitives, both LONG64 and ULONG64 data types will be
+	 * returned as long arrays. The application will have to deal with reinterpreting signed longs that are
+	 * negative as unsigned longs.
+	 * @return the data as an long array, or <code>null</code> if this makes no sense for the given type.
+	 */
+	public long[] getLongData();
+	
+	/**
+	 * Gets the raw data as a float array, if the type as indicated by the
+	 * header is appropriate.
+	 * @return the data as an double array, or <code>null</code> if this makes no sense for the given type.
+	 */
+	public float[] getFloatData();
+	
+	/**
+	 * Gets the raw data as a short array, if the type as indicated by the
+	 * header is appropriate.
+     * NOTE: since Java does not have unsigned primitives, both SHORT16 and USHORT16
+     * data types will be returned as short arrays. The application will have to deal
+     * with reinterpreting signed shorts that are negative as unsigned shorts.
+
+	 * @return the data as an short array, or <code>null</code> if this makes no sense for the given type.
+	 */
+	public short[] getShortData();
+	
+    /**
+     * Gets the raw data as a CompositeData object, if the type as indicated
+     * by the header is appropriate.<p>
+     *
+     * @return the data as a CompositeData object,
+     *         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;
+
+	/**
+	 * Get the description from the name provider (dictionary), if there is one.
+	 * @return the description from the name provider (dictionary), if there is one. If not, return
+	 *         NameProvider.NO_NAME_STRING.
+	 */
+	public String getDescription();
+
+
+
+}

jevio-base/src/main/java/org/jlab/coda/jevio
IEvioWriter.java 1.2 -> 1.3
diff -N IEvioWriter.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ IEvioWriter.java	28 Feb 2012 19:41:36 -0000	1.3
@@ -0,0 +1,18 @@
+package org.jlab.coda.jevio;
+
+import java.nio.ByteBuffer;
+
+/**
+ * This is implemented by objects that will be writing themselve's to en evio file.
+ * @author heddle
+ *
+ */
+public interface IEvioWriter {
+
+	/**
+	 * Write myself out a byte buffer.
+	 * @param byteBuffer the byteBuffer to write to.
+	 * @return the number of bytes written.
+	 */
+	public int write(ByteBuffer byteBuffer);
+}

jevio-base/src/main/java/org/jlab/coda/jevio
INameProvider.java 1.2 -> 1.3
diff -N INameProvider.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ INameProvider.java	28 Feb 2012 19:41:36 -0000	1.3
@@ -0,0 +1,26 @@
+package org.jlab.coda.jevio;
+
+/**
+ * This interface must be implemented by dictionary readers. For example, a dictionary reader that parses standard CODA
+ * dictionary plain text files, or a dictionary reader that processes the xml dictionary Maurizio uses for GEMC.
+ * 
+ * @author heddle
+ * 
+ */
+public interface INameProvider {
+
+    /**
+     * A string used to indicate that no name can be determined.
+     */
+    public static String NO_NAME_STRING = "???";
+
+	/**
+	 * Returns the pretty name of some evio structure. Typically this is involve
+	 * 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".
+	 */
+	public String getName(BaseStructure structure);
+}

jevio-base/src/main/java/org/jlab/coda/jevio
NameProvider.java 1.2 -> 1.3
diff -N NameProvider.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ NameProvider.java	28 Feb 2012 19:41:36 -0000	1.3
@@ -0,0 +1,58 @@
+package org.jlab.coda.jevio;
+
+/**
+ * This class maintains the single global NameProvider. There is no default name provider, so if the application doesn't
+ * provide one, the static method <code>getName</code> will always return the constant <code>NO_NAME_STRING</code>,
+ * which will be something like "NO_NAME_STRING".
+ * 
+ * Typically at start up an application will locate a dictionary file, use the <code>NameProviderFactory</code> to
+ * create a <code>INameProvider</code>, and then call the static method @link #setProvider(INameProvider).
+ * 
+ * @author heddle
+ * 
+ */
+public class NameProvider {
+
+	/**
+	 * The singleton
+	 */
+	private static INameProvider provider = null;
+
+	/**
+	 * Private constructor prevents anyone from making one of these.
+	 */
+	private NameProvider() {
+	}
+
+    /**
+     * Returns true if the provider (dictionary) was set, else false.
+     * @return <code>true</code> if the provider (dictionary) was set, else <code>false</code>.
+     */
+    public static boolean isProviderSet() {
+        return provider != null;
+    }
+
+    /**
+     * Sets the one global (singleton) name provider.
+     *
+     * @param aProvider the provider to use.
+     */
+    public static void setProvider(INameProvider aProvider) {
+        provider = aProvider;
+    }
+
+	/**
+	 * Returns the pretty name of some evio structure. Typically this is involve
+	 * 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".
+	 */
+	public static String getName(BaseStructure structure) {
+		if (provider == null) {
+			return INameProvider.NO_NAME_STRING;
+		}
+		return provider.getName(structure);
+	}
+}

jevio-base/src/main/java/org/jlab/coda/jevio
NameProviderFactory.java 1.2 -> 1.3
diff -N NameProviderFactory.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ NameProviderFactory.java	28 Feb 2012 19:41:36 -0000	1.3
@@ -0,0 +1,54 @@
+package org.jlab.coda.jevio;
+
+import java.io.File;
+
+/**
+ * A Factory class for generating an appropriate INameProvider. It makes its decisions based on the dictionary file that
+ * it gets handed in its only public method, <code>createNameProvider</code>. For example, if it is given an xml file
+ * (based on a ".xml" extension) it guesses that you want a reader that can handle the xml diction file developed for
+ * GEMC.
+ * 
+ * @author heddle
+ * 
+ */
+public class NameProviderFactory {
+
+	/**
+	 * Creates a NameProvider based on the file name.
+	 * 
+	 * @param file dictionary file.
+	 * @return a NameProvider appropriate for the fileName.
+	 */
+	public static INameProvider createNameProvider(File file) {
+        INameProvider provider = null;
+
+        if (file != null) {
+            // only handle xml files for now
+            if (file.getName().endsWith(".xml")) {
+                return new EvioXMLDictionary(file);
+            }
+            // TODO provide provider for standard CODA dictionaries
+        }
+
+        return provider;
+    }
+
+
+    /**
+     * Creates a NameProvider based on the file name.
+     *
+     * @param xmlString xml dictionary string.
+     * @return a NameProvider appropriate for the fileName.
+     */
+    public static INameProvider createNameProvider(String xmlString) {
+        INameProvider provider = null;
+
+        if (xmlString != null) {
+            return new EvioXMLDictionary(xmlString);
+            // TODO provide provider for standard CODA dictionaries
+        }
+
+        return provider;
+    }
+
+}

jevio-base/src/main/java/org/jlab/coda/jevio
SegmentHeader.java 1.2 -> 1.3
diff -N SegmentHeader.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ SegmentHeader.java	28 Feb 2012 19:41:36 -0000	1.3
@@ -0,0 +1,93 @@
+package org.jlab.coda.jevio;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * This the the header for an evio segment structure (<code>EvioSegment</code>). It does not contain the raw data, just the
+ * header. 
+ * 
+ * @author heddle
+ * 
+ */
+public final class SegmentHeader extends BaseStructureHeader {
+	
+	
+	/**
+	 * Null constructor.
+	 */
+	public SegmentHeader() {
+	}
+
+	/**
+	 * Constructor.
+	 * @param tag the tag for the segment header.
+	 * @param dataType the data type for the content of the segment.
+	 */
+	public SegmentHeader(int tag, DataType dataType) {
+		super(tag, dataType);
+	}
+
+    /**
+     * {@inheritDoc}
+     */
+    public int getHeaderLength() {return 1;}
+
+    /**
+     * {@inheritDoc}
+     */
+    protected void toArray(byte[] bArray, int offset, ByteOrder order) {
+        try {
+            if (order == ByteOrder.BIG_ENDIAN) {
+                bArray[offset]   = (byte)tag;
+                bArray[offset+1] = (byte)((dataType.getValue() & 0x3f) | (padding << 6));
+                ByteDataTransformer.toBytes((short)length, order, bArray, offset+2);
+            }
+            else {
+                ByteDataTransformer.toBytes((short)length, order, bArray, offset);
+                bArray[offset+2] = (byte)((dataType.getValue() & 0x3f) | (padding << 6));
+                bArray[offset+3] = (byte)tag;
+            }
+        }
+        catch (EvioException e) {e.printStackTrace();}
+    }
+
+	/**
+	 * Obtain a string representation of the bank header.
+	 * 
+	 * @return a string representation of the bank header.
+	 */
+	@Override
+	public String toString() {
+		StringBuffer sb = new StringBuffer(512);
+		sb.append(String.format("segment length: %d\n", length));
+		sb.append(String.format("number:         %d\n", number));
+		sb.append(String.format("data type:      %s\n", getDataTypeName()));
+		sb.append(String.format("tag:            %d\n", tag));
+        sb.append(String.format("padding:        %d\n", padding));
+		return sb.toString();
+	}
+	
+	/**
+	 * Write myself out a byte buffer. This write is relative--i.e., it uses the current position of the buffer.
+	 * 
+	 * @param byteBuffer the byteBuffer to write to.
+	 * @return the number of bytes written, which for a SegmentHeader is 4.
+	 */
+	@Override
+	public int write(ByteBuffer byteBuffer) {
+        if (byteBuffer.order() == ByteOrder.BIG_ENDIAN) {
+            byteBuffer.put((byte)tag);
+            byteBuffer.put((byte)((dataType.getValue() & 0x3f) | (padding << 6)));
+            byteBuffer.putShort((short)length);
+        }
+        else {
+            byteBuffer.putShort((short)length);
+            byteBuffer.put((byte)((dataType.getValue() & 0x3f) | (padding << 6)));
+            byteBuffer.put((byte)tag);
+        }
+		return 4;
+	}
+
+
+}
\ No newline at end of file

jevio-base/src/main/java/org/jlab/coda/jevio
StructureFinder.java 1.2 -> 1.3
diff -N StructureFinder.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ StructureFinder.java	28 Feb 2012 19:41:36 -0000	1.3
@@ -0,0 +1,137 @@
+package org.jlab.coda.jevio;
+
+import java.util.List;
+
+/**
+ * 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>
+ * method on the provided <code>EvioEvent</code> object by constructing the
+ * appropriate filter.
+ *
+ * @author heddle
+ * @author timmer
+ */
+public class StructureFinder {
+
+	
+	/**
+	 * Collect all the structures in an event that pass a filter.
+	 * @param event the event 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) {
+			return null;
+		}
+		return event.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 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) {
+		IEvioFilter filter = new IEvioFilter() {
+			@Override
+			public boolean accept(StructureType type, IEvioStructure struct) {
+				return (type == StructureType.BANK) &&
+                       (tag == struct.getHeader().tag) &&
+                       (number == struct.getHeader().number);
+			}
+		};
+		return getMatchingStructures(event, filter);
+	}	
+	
+	/**
+	 * Collect all the structures in an event that match a provided tag in their header.
+	 * @param event the event 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) {
+		IEvioFilter filter = new IEvioFilter() {
+			@Override
+            public boolean accept(StructureType type, IEvioStructure struct) {
+				return (tag == struct.getHeader().tag);
+			}
+		};
+		return getMatchingStructures(event, 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 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) {
+		IEvioFilter filter = new IEvioFilter() {
+			@Override
+            public boolean accept(StructureType type, IEvioStructure struct) {
+				return (type != StructureType.BANK) && (tag == struct.getHeader().tag);
+			}
+		};
+		return getMatchingStructures(event, 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.
+     * @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
+     * @throws EvioException if no dictionary is defined
+     */
+    public static List<BaseStructure> getMatchingStructures(EvioEvent event, String description,
+                                                            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 that match the given dictionary description.
+        class myEvioFilter implements IEvioFilter {
+            String description;
+            INameProvider dictionary;
+            boolean useGlobalDictionary;
+
+            myEvioFilter(boolean useGlobalDictionary, String description, INameProvider dictionary) {
+                this.description = description;
+                this.dictionary  = dictionary;
+                this.useGlobalDictionary = useGlobalDictionary;
+            }
+
+            public boolean accept(StructureType structureType, IEvioStructure structure) {
+                String desc;
+                if (useGlobalDictionary) {
+                    desc = NameProvider.getName((BaseStructure)structure);
+                }
+                else {
+                    desc = dictionary.getName((BaseStructure)structure);
+                }
+
+                // if this child matches the description add it to the list
+                return description.equals(desc);
+            }
+        };
+
+        myEvioFilter filter = new myEvioFilter(useGlobalDictionary, description, dictionary);
+        return getMatchingStructures(event, filter);
+    }
+
+}

jevio-base/src/main/java/org/jlab/coda/jevio
StructureTransformer.java added at 1.1
diff -N StructureTransformer.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ StructureTransformer.java	28 Feb 2012 19:41:36 -0000	1.1
@@ -0,0 +1,208 @@
+package org.jlab.coda.jevio;
+
+/**
+ * This class contains methods to transform structures from one type to another,
+ * for example changing an EvioSegment into an EvioBank.
+ *
+ * Date: Oct 1, 2010
+ * @author timmer
+ */
+public class StructureTransformer {
+
+    /**
+     * Create an EvioBank object from an EvioSegment. The new object has all
+     * data copied over, <b>except</b> that the segment's children were are added
+     * (not deep cloned) to the bank. Because a segment has no num, the user
+     * supplies that as an arg.
+     *
+     * @param segment EvioSegment object to transform.
+     * @param num num of the created EvioBank.
+     * @return the created EvioBank.
+     */
+    public EvioBank transform(EvioSegment segment, int num) {
+        BaseStructureHeader header = segment.getHeader();
+        DataType type = header.getDataType();
+
+        BankHeader bankHeader = new BankHeader(header.getTag(), type, num);
+        bankHeader.setLength(header.getLength()+1);
+        bankHeader.setPadding(header.getPadding());
+
+        EvioBank bank = new EvioBank(bankHeader);
+        bank.copy(segment);
+        return bank;
+    }
+
+
+    /**
+     * Create an EvioBank object from an EvioTagSegment. The new object has all
+     * data copied over, <b>except</b> that the tagsegment's children were are added
+     * (not deep cloned) to the bank. Because a segment has no num, the user
+     * supplies that as an arg.<p>
+     *
+     * NOTE: A tagsegment has no associated padding data. However,
+     * if a tagsegment is read from a file/buffer, padding info is already lost (=0), and
+     * if a tagsegment is created "by hand", the padding has already been calculated
+     * and exists in the header.
+     *
+     * @param tagsegment EvioTagSegment object to transform.
+     * @param num num of the created EvioBank.
+     * @return the created EvioBank.
+     */
+    public EvioBank transform(EvioTagSegment tagsegment, int num) {
+        BaseStructureHeader header = tagsegment.getHeader();
+        DataType type = header.getDataType();
+
+        BankHeader bankHeader = new BankHeader(header.getTag(), type, num);
+        bankHeader.setLength(header.getLength()+1);
+        bankHeader.setPadding(header.getPadding());
+
+        EvioBank bank = new EvioBank(bankHeader);
+        bank.copy(tagsegment);
+        return bank;
+    }
+
+
+    /**
+     * Create an EvioTagSegment object from an EvioSegment. The new object has all
+     * data copied over, <b>except</b> that the segment's children were are added
+     * (not deep cloned) to the tagsegment.<p>
+     *
+     * NOTE: No data should be lost in this transformaton since even though the
+     * segment has 6 bits of data type while the tag segment has only 4, only 4 bits
+     * are needed to contain the type data. And, the segment's tag is 8 bits while
+     * the tagsegment's tag is 12 bits so no problem there.
+     *
+     * @param segment EvioSegment object to transform.
+     * @return the created EvioTagSegment.
+     */
+    public EvioTagSegment transform(EvioSegment segment) {
+        BaseStructureHeader segHeader = segment.getHeader();
+        DataType type;
+        DataType segType = type = segHeader.getDataType();
+        
+        // Change 6 bit content type to 4 bits. Do this by changing
+        // ALSOBANK to BANK, ALSOSEGMENT to SEGMENT (ALSOTAGSEGMENT already removed)
+        if (segType == DataType.ALSOBANK) {
+            type = DataType.BANK;
+        }
+        else if (segType == DataType.ALSOSEGMENT) {
+            type = DataType.SEGMENT;
+        }
+
+        // 8 bit segment tag now becomes 12 bits
+        TagSegmentHeader tagsegHeader = new TagSegmentHeader(segHeader.getTag(), type);
+        tagsegHeader.setLength(segHeader.getLength());
+
+        EvioTagSegment tagseg = new EvioTagSegment(tagsegHeader);
+        tagseg.copy(segment);
+        return tagseg;
+    }
+
+    /**
+     * Create an EvioSegment object from an EvioTagSegment. The new object has all
+     * data copied over, <b>except</b> that the tagsegment's children were are added
+     * (not deep cloned) to the segment.<p>
+     *
+     * NOTE: A tagsegment has no associated padding data. However,
+     * if a tagsegment is read from a file/buffer, padding info is already lost (=0), and
+     * if a tagsegment is created "by hand", the padding has already been calculated
+     * and exists in the header. It is also possible that data is lost in this
+     * transformaton since the segment's tag is 8 bits while the tagsegment's tag is
+     * 12 bits. The user can override the truncation of the tagsegment's tag and simply
+     * set the created segment's tag by calling segment.getHeader().setTag(tag).
+     *
+     * @param tagsegment EvioTagSegment object to transform.
+     * @return the created EvioSegment.
+     */
+    public EvioSegment transform(EvioTagSegment tagsegment) {
+        BaseStructureHeader tagsegHeader = tagsegment.getHeader();
+        DataType type = tagsegHeader.getDataType();
+
+        // A tagseg tag is 12 bits which must be truncated to the seg's 8 bits.
+        // The user can override this by setting the resultant segment's tag by hand.
+        SegmentHeader segHeader = new SegmentHeader(tagsegHeader.getTag(), type);
+        segHeader.setLength(tagsegHeader.getLength());
+        segHeader.setPadding(tagsegHeader.getPadding());
+
+        EvioSegment seg = new EvioSegment(segHeader);
+        seg.copy(tagsegment);
+        return seg;
+    }
+
+    /**
+     * Create an EvioSegment object from an EvioBank. The new object has all
+     * data copied over, <b>except</b> that the bank's children were are added
+     * (not deep cloned) to the segment.<p>
+     *
+     * NOTE: It is possible that data is lost in this transformaton since the
+     * segment's tag is 8 bits while the bank's tag is 16 bits. To override the
+     * truncation of the tag, simply set the created segment's tag by calling
+     * segment.getHeader().setTag(tag). It is also possible that the length of
+     * the bank (32 bits) is too big for the segment (16 bits). This condition
+     * will cause an exception.
+     *
+     * @param bank EvioBank object to transform.
+     * @return the created EvioSegment.
+     * @throws EvioException if the bank is too long to change into a segment
+     */
+    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");
+        }
+        DataType type = header.getDataType();
+
+        // 16 bit bank tag now becomes 8 bits
+        SegmentHeader segHeader = new SegmentHeader(bank.getHeader().getTag(), type);
+        segHeader.setLength(header.getLength()-1);
+        segHeader.setPadding(header.getPadding());
+
+        EvioSegment seg = new EvioSegment(segHeader);
+        seg.copy(bank);
+        return seg;
+    }
+
+    /**
+     * Create an EvioTagSegment object from an EvioBank. The new object has all
+     * data copied over, <b>except</b> that the bank's children were are added
+     * (not deep cloned) to the segment.<p>
+     *
+     * NOTE: It is possible that data is lost in this transformaton since the
+     * tagsegment's tag is 12 bits while the bank's tag is 16 bits. To override the
+     * truncation of the tag, simply set the created tagsegment's tag by calling
+     * tagsegment.getHeader().setTag(tag). It is also possible that the length of
+     * the bank (32 bits) is too big for the tagsegment (16 bits). This condition
+     * will cause an exception.
+     *
+     * @param bank EvioBank object to transform.
+     * @param dummy only used to distinguish this method from {@link #transform(EvioBank)}.
+     * @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 {
+        BaseStructureHeader header = bank.getHeader();
+        if (header.getLength() > 65535) {
+            throw new EvioException("Bank is too long to transform into tagsegment");
+        }
+        DataType type;
+        DataType bankType = type = header.getDataType();
+
+        // Change 6 bit content type to 4 bits. Do this by changing
+        // ALSOBANK to BANK, ALSOSEGMENT to SEGMENT (ALSOTAGSEGMENT already removed)
+        if (bankType == DataType.ALSOBANK) {
+            type = DataType.BANK;
+        }
+        else if (bankType == DataType.ALSOSEGMENT) {
+            type = DataType.SEGMENT;
+        }
+
+        // 16 bit bank tag now becomes 12 bits
+        TagSegmentHeader tagsegHeader = new TagSegmentHeader(bank.getHeader().getTag(), type);
+        tagsegHeader.setLength(header.getLength()-1);
+        tagsegHeader.setPadding(header.getPadding());
+
+        EvioTagSegment tagseg = new EvioTagSegment(tagsegHeader);
+        tagseg.copy(bank);
+        return tagseg;
+    }
+}

jevio-base/src/main/java/org/jlab/coda/jevio
StructureType.java 1.2 -> 1.3
diff -N StructureType.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ StructureType.java	28 Feb 2012 19:41:36 -0000	1.3
@@ -0,0 +1,63 @@
+package org.jlab.coda.jevio;
+
+/**
+ * This an enum used to convert structure type numerical values to a more meaningful name. For example, the structure
+ * type with value 0xe corresponds to the enum BANK. Mostly this is used for printing.
+ * 
+ * @author heddle
+ * 
+ */
+public enum StructureType {
+
+	UNKNOWN32(0x0), 
+	TAGSEGMENT(0xc), 
+	SEGMENT(0xd), 
+	BANK(0xe);
+
+	private int value;
+
+	private StructureType(int value) {
+		this.value = value;
+	}
+
+	/**
+	 * Get the enum's value.
+	 * 
+	 * @return the value, e.g., 0xe for a BANK
+	 */
+	public int getValue() {
+		return value;
+	}
+
+	/**
+	 * Obtain the name from the value.
+	 * 
+	 * @param value the value to match.
+	 * @return the name, or "UNKNOWN".
+	 */
+	public static String getName(int value) {
+		StructureType structuretypes[] = StructureType.values();
+		for (StructureType dt : structuretypes) {
+			if (dt.value == value) {
+				return dt.name();
+			}
+		}
+		return "UNKNOWN";
+	}
+
+	/**
+	 * Obtain the enum from the value.
+	 * 
+	 * @param value the value to match.
+	 * @return the matching enum, or <code>null</code>.
+	 */
+	public static StructureType getStructureType(int value) {
+		StructureType structuretypes[] = StructureType.values();
+		for (StructureType dt : structuretypes) {
+			if (dt.value == value) {
+				return dt;
+			}
+		}
+		return null;
+	}
+}

jevio-base/src/main/java/org/jlab/coda/jevio
TagSegmentHeader.java 1.2 -> 1.3
diff -N TagSegmentHeader.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ TagSegmentHeader.java	28 Feb 2012 19:41:36 -0000	1.3
@@ -0,0 +1,91 @@
+package org.jlab.coda.jevio;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * This the the header for an evio tag segment structure (<code>EvioTagSegment</code>). It does not contain the raw data, just the
+ * header. 
+ * 
+ * @author heddle
+ * 
+ */
+public final class TagSegmentHeader extends BaseStructureHeader {
+	
+	/**
+	 * Null constructor.
+	 */
+	public TagSegmentHeader() {
+	}
+
+	/**
+	 * Constructor.
+	 * @param tag the tag for the tag segment header.
+	 * @param dataType the data type for the content of the tag segment.
+	 */
+	public TagSegmentHeader(int tag, DataType dataType) {
+        super(tag, dataType);
+	}
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public int getHeaderLength() {return 1;}
+
+    /**
+     * {@inheritDoc}
+     */
+    protected void toArray(byte[] bArray, int offset, ByteOrder order) {
+        short compositeWord = (short) ((tag << 4) | (dataType.getValue() & 0xf));
+
+        try {
+            if (order == ByteOrder.BIG_ENDIAN) {
+                ByteDataTransformer.toBytes(compositeWord, order, bArray, offset);
+                ByteDataTransformer.toBytes((short)length, order, bArray, offset + 2);
+            }
+            else {
+                ByteDataTransformer.toBytes((short)length, order, bArray, offset);
+                ByteDataTransformer.toBytes(compositeWord, order, bArray, offset+2);
+            }
+        }
+        catch (EvioException e) {e.printStackTrace();}
+    }
+    
+	/**
+	 * Obtain a string representation of the bank header.
+	 * 
+	 * @return a string representation of the bank header.
+	 */
+	@Override
+	public String toString() {
+		StringBuffer sb = new StringBuffer(512);
+		sb.append(String.format("tag-seg length: %d\n", length));
+		sb.append(String.format("number:         %d\n", number));
+		sb.append(String.format("data type:      %s\n", getDataTypeName()));
+		sb.append(String.format("tag:            %d\n", tag));
+		return sb.toString();
+	}
+	
+	/**
+	 * Write myself out a byte buffer. This write is relative--i.e., it uses the current position of the buffer.
+	 * 
+	 * @param byteBuffer the byteBuffer to write to.
+	 * @return the number of bytes written, which for a TagSegmentHeader is 4.
+	 */
+	@Override
+	public int write(ByteBuffer byteBuffer) {
+		short compositeWord = (short) ((tag << 4) | (dataType.getValue() & 0xf));
+
+        if (byteBuffer.order() == ByteOrder.BIG_ENDIAN) {
+            byteBuffer.putShort(compositeWord);
+            byteBuffer.putShort((short)length);
+        }
+        else {
+            byteBuffer.putShort((short)length);
+            byteBuffer.putShort(compositeWord);
+        }
+		return 4;
+	}
+
+}
\ No newline at end of file

jevio-base/src/main/java/org/jlab/coda/jevio/graphics
EventInfoPanel.java added at 1.1
diff -N EventInfoPanel.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ EventInfoPanel.java	28 Feb 2012 19:41:37 -0000	1.1
@@ -0,0 +1,184 @@
+package org.jlab.coda.jevio.graphics;
+
+import javax.swing.*;
+import javax.swing.border.EmptyBorder;
+import java.awt.*;
+
+/**
+ * This is a panel that displays evio event info (event source, dictionary source, event number, and
+ * number of events) at the top of the GUI. 
+ */
+public class EventInfoPanel extends JPanel {
+
+    /**
+     * A label for displaying the current event source name.
+     */
+    private NamedLabel eventSourceLabel;
+
+    /**
+     * A label for displaying the dictionary source.
+     */
+    private NamedLabel dictionaryLabel;
+
+    /**
+     * A label for displaying the number of events in an event file or the number of
+     * cMsg messages in a queue.
+     */
+    private NamedLabel numEventsLabel;
+
+    /**
+     * A label for displaying the ordinal number of the event from an event file.
+     */
+    private NamedLabel eventNumberLabel;
+
+
+    /**
+     * Create the panel that goes in the north - top of the GUI. This will hold 4 labels.
+     * One showing the current event source. The second showing the current dictionary source.
+     * The third showing the event number, and the fourth showing the number of events.
+     *
+     * @return the panel.
+     */
+    public EventInfoPanel() {
+
+        setLayout(new GridLayout(2, 1, 0, 3)); // rows, cols, hgap, vgap
+        setBorder(new EmptyBorder(5, 200, 5, 0)); // top, left, bot, right
+
+        eventSourceLabel = new NamedLabel("event source", "event_source", 430);
+        dictionaryLabel  = new NamedLabel("dictionary", "event_source", 430);
+
+        eventNumberLabel = new NamedLabel("event#", "num_events", 150);
+        numEventsLabel   = new NamedLabel("num events", "num_events", 150);
+
+        // limit size of labels
+        Dimension d1 = eventSourceLabel.getPreferredSize();
+        Dimension d2 = eventNumberLabel.getPreferredSize();
+
+        eventSourceLabel.setMaximumSize(d1);
+        dictionaryLabel.setMaximumSize(d1);
+        eventNumberLabel.setMaximumSize(d2);
+        numEventsLabel.setMaximumSize(d2);
+
+        // two sub panels
+        JPanel p0 = new JPanel();
+        p0.setLayout(new BoxLayout(p0, BoxLayout.X_AXIS));
+		p0.add(eventSourceLabel);
+        p0.add(Box.createRigidArea(new Dimension(5,0)));
+		p0.add(eventNumberLabel);
+
+        JPanel p1 = new JPanel();
+        p1.setLayout(new BoxLayout(p1, BoxLayout.X_AXIS));
+		p1.add(dictionaryLabel);
+        p1.add(Box.createRigidArea(new Dimension(5,0)));
+		p1.add(numEventsLabel);
+
+		add(p0);
+		add(p1);
+    }
+
+    /**
+     * Set this panel's displayed values.
+     *
+     * @param source source of viewed event.
+     * @param eventNumber event number of event currently displayed.
+     * @param numberOfEvents number of events if file, else 1.
+     * @param dictionary dictionary source: name of dictionary file or,
+     *                   "in message" if dictionary came from cMsg message.
+     */
+    public void setDisplay(String source, int eventNumber, int numberOfEvents, String dictionary) {
+        if (source != null) {
+            eventSourceLabel.setText(source);
+        }
+        if (dictionary != null) {
+            dictionaryLabel.setText(dictionary);
+        }
+        eventNumberLabel.setText("" + eventNumber);
+        numEventsLabel.setText("" + numberOfEvents);
+    }
+
+    /**
+     * Set some of this panel's displayed values.
+     *
+     * @param source source of viewed event.
+     * @param numberOfEvents number of events if file, else 1.
+     */
+    public void setDisplay(String source, int numberOfEvents) {
+        if (source != null) {
+            eventSourceLabel.setText(source);
+        }
+        numEventsLabel.setText("" + numberOfEvents);
+    }
+
+    /**
+     * Set the displayed dictionary source value.
+     * @param dictionary dictionary source.
+     */
+    public void setDictionary(String dictionary) {
+        if (dictionary != null) {
+            dictionaryLabel.setText(dictionary);
+        }
+    }
+
+    /**
+     * Get the displayed dictionary source value.
+     * @return the displayed dictionary source value.
+     */
+    public String getDictionary() {
+        return dictionaryLabel.getText();
+    }
+
+    /**
+     * Set the displayed event source value.
+     * @param source event source.
+     */
+    public void setSource(String source) {
+        if (source != null) {
+            eventSourceLabel.setText(source);
+        }
+    }
+
+    /**
+     * Get the displayed event source value.
+     * @return the displayed event source value.
+     */
+    public String getSource() {
+        return eventSourceLabel.getText();
+    }
+
+    /**
+     * Set the displayed event number value.
+     * @param eventNumber event number.
+     */
+    public void setEventNumber(int eventNumber) {
+        if (eventNumber > -1) {
+            eventNumberLabel.setText("" + eventNumber);
+        }
+    }
+
+    /**
+     * Get the displayed event number value.
+     * @return the displayed event number value.
+     */
+    public int getEventNumber() {
+        return Integer.parseInt(eventNumberLabel.getText());
+    }
+
+    /**
+     * Set the displayed number-of-events value.
+     * @param numberOfEvents number of events.
+     */
+    public void setNumberOfEvents(int numberOfEvents) {
+        if (numberOfEvents > -1) {
+            numEventsLabel.setText("" + numberOfEvents);
+        }
+    }
+
+    /**
+     * Get the displayed number-of-events value.
+     * @return the displayed number-of-events value.
+     */
+    public int getNumberOfEvents() {
+        return Integer.parseInt(numEventsLabel.getText());
+    }
+
+}

jevio-base/src/main/java/org/jlab/coda/jevio/graphics
EventSource.java added at 1.1
diff -N EventSource.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ EventSource.java	28 Feb 2012 19:41:37 -0000	1.1
@@ -0,0 +1,12 @@
+package org.jlab.coda.jevio.graphics;
+
+/**
+ * This enum contains the choices for a source of events to view.
+ * @author timmer
+ * @date Oct 20, 2009
+ */
+public enum EventSource {
+    FILE,
+    CMSG,
+    ET;
+}

jevio-base/src/main/java/org/jlab/coda/jevio/graphics
EventTreePanel.java added at 1.1
diff -N EventTreePanel.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ EventTreePanel.java	28 Feb 2012 19:41:37 -0000	1.1
@@ -0,0 +1,436 @@
+package org.jlab.coda.jevio.graphics;
+
+import java.awt.*;
+
+import javax.swing.*;
+import javax.swing.border.TitledBorder;
+import javax.swing.event.TreeSelectionEvent;
+import javax.swing.event.TreeSelectionListener;
+import javax.swing.tree.TreeSelectionModel;
+
+import org.jlab.coda.jevio.*;
+
+/**
+ * This is a simple GUI that displays an evio event in a tree.
+ * It allows the user to open event files and dictionaries,
+ * and go event-by-event showing the event in a JTree.
+ * @author Heddle
+ */
+@SuppressWarnings("serial")
+public class EventTreePanel extends JPanel implements TreeSelectionListener {
+
+
+    /**
+     * Panel for displaying header information.
+     */
+    private HeaderPanel headerPanel = new HeaderPanel();
+
+    /**
+     * Panel for displaying event information.
+     */
+    private EventInfoPanel eventInfoPanel = new EventInfoPanel();
+
+	/**
+	 * The actual graphical tree object.
+	 */
+	private JTree tree;
+
+    /**
+	 * The current event.
+	 */
+	private EvioEvent event;
+
+	/**
+	 * Text area shows data values for selected nodes.
+	 */
+	private JTextArea textArea;
+
+    /**
+     * A shared progress bar.
+     */
+    private JProgressBar progressBar;
+
+    /**
+     * View ints in hexadecimal or decimal?
+     */
+    private boolean intsInHex;
+
+
+
+    /**
+	 * Constructor for a simple tree viewer for evio files.
+	 */
+	public EventTreePanel() {
+		setLayout(new BorderLayout());
+		// add all the components
+		addComponents();
+	}
+
+    /**
+     * Set wether integer data is displayed in hexidecimal or decimal.
+     * @param intsInHex if <code>true</code> then display as hex, else deciaml
+     */
+    public void setIntsInHex(boolean intsInHex) {
+        this.intsInHex = intsInHex;
+    }
+
+
+    /**
+     * Get the panel displaying the event information.
+     * @return
+     */
+    public EventInfoPanel getEventInfoPanel() {
+        return eventInfoPanel;
+    }
+
+    /**
+     * Get the panel displaying header information.
+     * @return
+     */
+    public HeaderPanel getHeaderPanel() {
+        return headerPanel;
+    }
+
+    /**
+     * Get the progress bar.
+     * @return the progress bar.
+     */
+    public JProgressBar getProgressBar() {
+        return progressBar;
+    }
+
+    /**
+     * Refresh textArea display.
+     */
+    public void refreshDisplay() {
+        valueChanged(null);
+    }
+
+    /**
+     * Refresh description (dictionary) display.
+     */
+    public void refreshDescription() {
+        headerPanel.setDescription(event);
+    }
+
+    /**
+     * Add the components to this panel.
+	 */
+	protected void addComponents() {
+		add(eventInfoPanel, BorderLayout.NORTH);
+		add(createTree(), BorderLayout.CENTER);
+        add(createTextArea(), BorderLayout.WEST);
+
+        // define south
+		progressBar = new JProgressBar();
+		progressBar.setBorder(BorderFactory.createTitledBorder(null, "progress", TitledBorder.LEADING,
+				              TitledBorder.TOP, null, Color.blue));
+        progressBar.setPreferredSize(new Dimension(200, 20));
+
+        JPanel panel = new JPanel();
+        panel.setLayout(new BorderLayout());
+        panel.add(headerPanel, BorderLayout.CENTER);
+        panel.add(progressBar, BorderLayout.WEST);
+
+        add(panel, BorderLayout.SOUTH);
+	}
+
+	/**
+	 * Create the tree that will display the event. What is actually
+     * returned is the scroll pane that contains the tree.
+	 *
+	 * @return the scroll pane holding the event tree.
+	 */
+	private JScrollPane createTree() {
+		tree = new JTree();
+		tree.setModel(null);
+
+		tree.setBorder(BorderFactory.createTitledBorder(null, "Tree representation of the EVIO event",
+				TitledBorder.LEADING, TitledBorder.TOP, null, Color.blue));
+
+		tree.putClientProperty("JTree.lineStyle", "Angled");
+		tree.setShowsRootHandles(true);
+		tree.setEditable(false);
+		tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
+		tree.addTreeSelectionListener(this);
+
+		JScrollPane scrollPane = new JScrollPane();
+		scrollPane.getViewport().setView(tree);
+		return scrollPane;
+	}
+
+	/**
+	 * Create the text area that will display structure data. What is actually
+     * returned is the scroll pane that contains the text area.
+	 *
+	 * @return the scroll pane holding the text area.
+	 */
+	private JScrollPane createTextArea() {
+
+		textArea = new JTextArea();
+		textArea.setBorder(BorderFactory.createTitledBorder(null, "Array Data",
+                                                            TitledBorder.LEADING,
+                                                            TitledBorder.TOP,
+                                                            null, Color.blue));
+        textArea.setEditable(false);
+        
+		JScrollPane scrollPane = new JScrollPane();
+		scrollPane.getViewport().setView(textArea);
+        // Borderlayout respects preferred width in east/west,
+        // but ignores height -- so use this to set width only.
+        // Don't use "setPreferredSize" on textArea or it messes up the scrolling.
+        scrollPane.setPreferredSize(new Dimension(200,1000));
+
+		return scrollPane;
+	}
+
+
+	/**
+	 * Selection event on our tree.
+	 *
+	 * @param treeSelectionEvent the causal event.
+	 */
+	@Override
+	public void valueChanged(TreeSelectionEvent treeSelectionEvent) {
+
+		BaseStructure structure = (BaseStructure) tree.getLastSelectedPathComponent();
+		textArea.setText("");
+        int blankLineEveryNth = 5; // put in a blank line after every Nth element listed
+
+		if (structure == null) {
+			return;
+		}
+		headerPanel.setHeader(structure);
+
+		if (structure.isLeaf()) {
+
+            int lineCounter=1, index=1;
+			BaseStructureHeader header = structure.getHeader();
+
+			switch (header.getDataType()) {
+			case DOUBLE64:
+				double doubledata[] = structure.getDoubleData();
+				if (doubledata != null) {
+					for (double d : doubledata) {
+						String s = String.format("[%02d]  %15.11e", index++, d);
+						textArea.append(s);
+                        if (lineCounter < doubledata.length) {
+                            if (lineCounter % blankLineEveryNth == 0) {
+                                textArea.append("\n\n");
+                            }
+                            else {
+                                textArea.append("\n");
+                            }
+                            lineCounter++;
+                        }
+					}
+				}
+				else {
+					textArea.append("null data\n");
+				}
+				break;
+
+			case FLOAT32:
+				float floatdata[] = structure.getFloatData();
+				if (floatdata != null) {
+					for (float d : floatdata) {
+						String s = String.format("[%02d]  %10.6e", index++, d);
+						textArea.append(s);
+                        if (lineCounter < floatdata.length) {
+                            if (lineCounter % blankLineEveryNth == 0) {
+                                textArea.append("\n\n");
+                            }
+                            else {
+                                textArea.append("\n");
+                            }
+                            lineCounter++;
+                        }
+					}
+				}
+				else {
+					textArea.append("null data\n");
+				}
+				break;
+
+			case LONG64:
+			case ULONG64:
+				long longdata[] = structure.getLongData();
+				if (longdata != null) {
+					for (long i : longdata) {
+						String s;
+                        if (intsInHex) {
+                            s = String.format("[%02d]  %#018X", index++, i);
+                        }
+                        else {
+                            s = String.format("[%02d]  %d", index++, i);
+                        }
+						textArea.append(s);
+                        if (lineCounter < longdata.length) {
+                            if (lineCounter % blankLineEveryNth == 0) {
+                                textArea.append("\n\n");
+                            }
+                            else {
+                                textArea.append("\n");
+                            }
+                            lineCounter++;
+                        }
+					}
+				}
+				else {
+					textArea.append("null data\n");
+				}
+				break;
+
+			case INT32:
+			case UINT32:
+				int intdata[] = structure.getIntData();
+				if (intdata != null) {
+					for (int i : intdata) {
+						String s;
+                        if (intsInHex) {
+                            s = String.format("[%02d]  %#010X", index++, i);
+                        }
+                        else {
+                            s = String.format("[%02d]  %d", index++, i);
+                        }
+                        textArea.append(s);
+                        if (lineCounter < intdata.length) {
+                            if (lineCounter % blankLineEveryNth == 0) {
+                                textArea.append("\n\n");
+                            }
+                            else {
+                                textArea.append("\n");
+                            }
+                            lineCounter++;
+                        }
+                    }
+				}
+				else {
+					textArea.append("null data\n");
+				}
+				break;
+
+			case SHORT16:
+			case USHORT16:
+				short shortdata[] = structure.getShortData();
+				if (shortdata != null) {
+					for (short i : shortdata) {
+						String s;
+                        if (intsInHex) {
+                            s = String.format("[%02d]  %#06X", index++, i);
+                        }
+                        else {
+                            s = String.format("[%02d]  %d", index++, i);
+                        }
+						textArea.append(s);
+                        if (lineCounter < shortdata.length) {
+                            if (lineCounter % blankLineEveryNth == 0) {
+                                textArea.append("\n\n");
+                            }
+                            else {
+                                textArea.append("\n");
+                            }
+                            lineCounter++;
+                        }
+					}
+				}
+				else {
+					textArea.append("null data\n");
+				}
+				break;
+
+			case CHAR8:
+			case UCHAR8:
+				byte bytedata[] = structure.getByteData();
+				if (bytedata != null) {
+					for (byte i : bytedata) {
+						String s = String.format("[%02d]  %d", index++, i);
+						textArea.append(s);
+                        if (lineCounter < bytedata.length) {
+                            if (lineCounter % blankLineEveryNth == 0) {
+                                textArea.append("\n\n");
+                            }
+                            else {
+                                textArea.append("\n");
+                            }
+                            lineCounter++;
+                        }
+					}
+				}
+				else {
+					textArea.append("null data\n");
+				}
+				break;
+
+			case CHARSTAR8:
+                String stringdata[] = structure.getStringData();
+                for (String str : stringdata) {
+                    String s = String.format("[%02d]  %s\n", index++, str);
+                    textArea.append(s != null ? s : "null data\n");
+                }
+				break;
+
+            case COMPOSITE:
+                try {
+                    CompositeData cData = structure.getCompositeData();
+//                    for (String str : stringdata) {
+//                            String s = String.format("[%02d]  %s\n", index++, str);
+//                            textArea.append(s != null ? s : "null data\n");
+//                    }
+                }
+                catch (EvioException e) {
+                    // internal format error
+                }
+                break;
+
+            }
+
+		}
+		tree.repaint();
+	}
+
+
+
+	/**
+	 * Get the currently displayed event.
+	 *
+	 * @return the currently displayed event.
+	 */
+	public EvioEvent getEvent() {
+		return event;
+	}
+
+	/**
+	 * Set the currently displayed event.
+	 *
+	 * @param event the currently displayed event.
+	 */
+	public void setEvent(EvioEvent event) {
+		this.event = event;
+		if (event != null) {
+			tree.setModel(event.getTreeModel());
+			headerPanel.setHeader(event);
+			eventInfoPanel.setEventNumber(event.getEventNumber());
+		    expandAll();
+        }
+		else {
+			tree.setModel(null);
+			headerPanel.setHeader(null);
+            eventInfoPanel.setEventNumber(0);
+		}
+	}
+
+
+    /**
+     * Expand all nodes.
+     */
+    public void expandAll() {
+        if (tree != null) {
+            for (int i = 0; i < tree.getRowCount(); i++) {
+                tree.expandRow(i);
+            }
+        }
+    }
+
+
+}
\ No newline at end of file

jevio-base/src/main/java/org/jlab/coda/jevio/graphics
HeaderPanel.java added at 1.1
diff -N HeaderPanel.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ HeaderPanel.java	28 Feb 2012 19:41:37 -0000	1.1
@@ -0,0 +1,127 @@
+package org.jlab.coda.jevio.graphics;
+
+import java.awt.*;
+
+import javax.swing.*;
+import javax.swing.border.EmptyBorder;
+
+import org.jlab.coda.jevio.BaseStructure;
+import org.jlab.coda.jevio.BaseStructureHeader;
+
+/**
+ * This is a panel that displays the information from a BaseStructureHeader.
+ * @author heddle
+ *
+ */
+@SuppressWarnings("serial")
+public class HeaderPanel extends JPanel {
+
+    /** Number of bytes in selected structure. */
+	private NamedLabel lengthLabel;
+
+    /** Type of selected structure: BANK, SEGMENT, or TAGSEGMENT. */
+	private NamedLabel structureLabel;
+
+    /** Type of data in selected structure. */
+	private NamedLabel dataTypeLabel;
+
+    /** Tag of selected structure. */
+	private NamedLabel tagLabel;
+
+    /** Number (num) of selected structure. */
+	private NamedLabel numberLabel;
+
+    /** Description of selected structure from dictionary. */
+	private NamedLabel descriptionLabel;
+
+
+    /**
+     * Constructor.
+     */
+	public HeaderPanel() {
+        setLayout(new GridLayout(2, 1, 0, 3)); // rows, cols, hgap, vgap
+        setBorder(new EmptyBorder(5, 5, 2, 0));   // top, left, bot, right
+
+		structureLabel = new NamedLabel("structure",   "description", 150);
+        dataTypeLabel  = new NamedLabel("data type",   "description", 150);
+
+		tagLabel    = new NamedLabel(   "tag", "number", 150);
+		numberLabel = new NamedLabel("number", "number", 150);
+
+        lengthLabel      = new NamedLabel(     "length", "description", 200);
+        descriptionLabel = new NamedLabel("description", "description", 200);
+
+        // limit size of labels
+        Dimension d1 = structureLabel.getPreferredSize();
+        Dimension d2 = descriptionLabel.getPreferredSize();
+
+        structureLabel.setMaximumSize(d1);
+        dataTypeLabel.setMaximumSize(d1);
+        tagLabel.setMaximumSize(d1);
+        numberLabel.setMaximumSize(d1);
+        lengthLabel.setMaximumSize(d2);
+        descriptionLabel.setMaximumSize(d2);
+
+		JPanel p0 = createLayoutPanel();
+		JPanel p1 = createLayoutPanel();
+
+		p0.add(structureLabel);
+        p0.add(Box.createRigidArea(new Dimension(5,0)));
+		p0.add(tagLabel);
+        p0.add(Box.createRigidArea(new Dimension(5,0)));
+		p0.add(lengthLabel);
+
+		p1.add(dataTypeLabel);
+        p1.add(Box.createRigidArea(new Dimension(5,0)));
+		p1.add(numberLabel);
+        p1.add(Box.createRigidArea(new Dimension(5,0)));
+		p1.add(descriptionLabel);
+
+		add(p0);
+		add(p1);
+	}
+
+	private JPanel createLayoutPanel() {
+		JPanel p = new JPanel();
+        p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS));
+		return p;
+	}
+
+	/**
+	 * Set the fields in the panel based on the data in the header.
+	 * @param structure the structure to use to set the fields, mostly from its header.
+	 */
+	public void setHeader(BaseStructure structure) {
+
+		if ((structure == null) || (structure.getHeader() == null)) {
+			structureLabel.setText("   ");
+			lengthLabel.setText("   ");
+			tagLabel.setText("   ");
+			dataTypeLabel.setText("   ");
+			numberLabel.setText("   ");
+			descriptionLabel.setText("   ");
+		}
+		else {
+			BaseStructureHeader header = structure.getHeader();
+			structureLabel.setText("" + structure.getStructureType());
+			lengthLabel.setText(4*header.getLength() + " bytes");
+			tagLabel.setText("" + header.getTag());
+			dataTypeLabel.setText("" + header.getDataType());
+			numberLabel.setText("" + header.getNumber());
+			descriptionLabel.setText(structure.getDescription());
+		}
+	}
+
+    /**
+     * Set the dictionary description in header panel.
+     * @param structure event being described
+     */
+    public void setDescription(BaseStructure structure) {
+        if (structure == null) {
+            descriptionLabel.setText("   ");
+            return;
+        }
+        descriptionLabel.setText(structure.getDescription());
+    }
+
+}
\ No newline at end of file

jevio-base/src/main/java/org/jlab/coda/jevio/graphics
NamedLabel.java added at 1.1
diff -N NamedLabel.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ NamedLabel.java	28 Feb 2012 19:41:37 -0000	1.1
@@ -0,0 +1,83 @@
+package org.jlab.coda.jevio.graphics;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.FontMetrics;
+
+import javax.swing.*;
+
+/**
+ * A simple class for created a named label.
+ * @author heddle
+ */
+@SuppressWarnings("serial")
+public class NamedLabel extends JPanel {
+
+	/**
+	 * The label, on the right, that can be changed.
+	 */
+	private JTextField variableLabel;
+
+	/**
+	 * Create a NamedLabel--which has a fixed label (a prompt) and a variable label. It is not editable.
+	 *
+	 * @param name this is a constant label, like "event file:"
+	 * @param preferredWidth this is the preferred width of the variable label.
+	 */
+	public NamedLabel(String name, int preferredWidth) {
+		this(name, null, preferredWidth);
+	}
+
+	/**
+	 * Create a NamedLabel--which has a fixed label (a prompt) and a variable label. It is not editable.
+	 *
+	 * @param name this is a constant label, like "event file:"
+	 * @param sizingString this is used to size the fixed label, to help getting things to align.
+	 * @param preferredWidth this is the preferred width of the variable label.
+	 */
+	public NamedLabel(String name, String sizingString, int preferredWidth) {
+		JLabel fixedLabel = new JLabel(name);
+		fixedLabel.setForeground(Color.blue);
+
+		// try to size the fixed label?
+		if (sizingString != null) {
+			FontMetrics fm = getFontMetrics(fixedLabel.getFont());
+			int sw = 4 + fm.stringWidth(sizingString);
+			Dimension fd = fixedLabel.getPreferredSize();
+			fd.width = sw;
+			fixedLabel.setPreferredSize(fd);
+		}
+        
+        setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
+        add(fixedLabel);
+		variableLabel = new JTextField("     ");
+		variableLabel.setBackground(Color.white);
+		variableLabel.setOpaque(true);
+		Dimension d = variableLabel.getPreferredSize();
+		d.width = preferredWidth;
+		variableLabel.setPreferredSize(d);
+        variableLabel.setEditable(false);
+        add(variableLabel);
+		setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEtchedBorder(),
+                                                     BorderFactory.createEmptyBorder(3,5,3,5)));
+	}
+
+	/**
+	 * Set the text in the variable label.
+	 *
+	 * @param text the text to display in the variable label.
+	 */
+	public void setText(String text) {
+		variableLabel.setText(text);
+	}
+
+	/**
+	 * Get the text in the variable label.
+	 *
+	 * @return the text in the variable label.
+	 */
+	public String getText() {
+		return variableLabel.getText();
+	}
+
+}
\ No newline at end of file

jevio-base/src/main/java/org/jlab/coda/jevio/test
CompositeTester.java added at 1.1
diff -N CompositeTester.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ CompositeTester.java	28 Feb 2012 19:41:37 -0000	1.1
@@ -0,0 +1,324 @@
+package org.jlab.coda.jevio.test;
+
+import org.jlab.coda.jevio.*;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.IntBuffer;
+import java.util.List;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: timmer
+ * Date: 4/12/11
+ * Time: 10:21 AM
+ * To change this template use File | Settings | File Templates.
+ */
+public class CompositeTester {
+
+
+
+    /** For testing only */
+    public static void main(String args[]) {
+
+
+        int[] bank = new int[24];
+
+        /***********************/
+        /* bank of tagsegments */
+        /***********************/
+        bank[0] = 23;                       // bank length
+        bank[1] = 6 << 16 | 0xF << 8 | 3;   // tag = 6, bank contains composite type, num = 3
+
+        /* first part of composite type (for format) = tagseg (tag & type ignored, len used) */
+        //    tagseg[1] = 5 << 20 | 0x3 << 16 | 3; /* tag = 5, seg has char data, len = 2 */
+        /* ASCII chars values in latest evio string (array) format, 2(2I,F) */
+        //    tagseg[2] = 0x49 << 24 | 0x32 << 16 | 0x28 << 8 | 0x32 ;   /* I 2 ( 2 */
+        //     tagseg[3] =   0  << 24 | 0x29 << 16 | 0x46 << 8 | 0x2C ;   /* \0 ) F , */
+        //     tagseg[4] =   4  << 24 |    4 << 16 |    4 << 8 |    4 ;   /* \4 \4 \4 \4 */
+
+        /* second part of composite type (for data) = bank (tag, num, type ignored, len used) */
+        /*
+        intWord = &(bank[20]);
+        intWord[0] = 7;
+        intWord[1] = 6 << 16 | 0xF << 8 | 1;
+        data = 123.456;
+        intWord[2] = 0x1111;
+        intWord[3] = 0x2222;
+        intWord[4] = *(int *)&data;
+        intWord[5] = 0x11111111;
+        intWord[6] = 0x22222222;
+        intWord[7] = *(int *)&data;
+        */
+
+        // first part of composite type (for format) = tagseg (tag & type ignored, len used)
+        //    tagseg[1] = 5 << 20 | 0x3 << 16 | 3; /* tag = 5, seg has char data, len = 3
+        // ASCII chars values in latest evio string (array) format, I,N(I,F)
+        //    tagseg[2] = 0x28 << 24 | 0x4E << 16 | 0x2C << 8 | 0x49;    // ( N , I
+        //    tagseg[3] = 0x29 << 24 | 0x46 << 16 | 0x2C << 8 | 0x49;    // ) F , I
+        //    tagseg[4] =   4  << 24 |    4 << 16 |    4 << 8 |    0 ;   // \4 \4 \4 \0
+
+        // second part of composite type (for data) = bank (tag, num, type ignored, len used)
+        /*
+        intWord = &(bank[20]);
+        intWord[0] = 7;
+        intWord[1] = 6 << 16 | 0xF << 8 | 1;
+        data = 123.456;
+        intWord[2] = 0x3333;
+        intWord[3] = 0x2;
+        intWord[4] = 0x1111;
+        intWord[5] = *(int *)&data;
+        intWord[6] = 0x2222;
+        intWord[7] = *(int *)&data;
+        */
+
+        /*
+        // first part of composite type (for format) = tagseg (tag & type ignored, len used)
+        bank[2]  = 5 << 20 | 0x3 << 16 | 3; // tag = 5, seg has char data, len = 3
+        // ASCII chars values in latest evio string (array) format, N(N(I,2S)) with N=2
+        bank[3]  = 0x28 << 24 | 0x4E << 16 | 0x28 << 8 | 0x4E;    // ( N ( N
+        bank[4]  = 0x53 << 24 | 0x32 << 16 | 0x2C << 8 | 0x49;    // S 2 , I
+        bank[5]  =   4  << 24 |    0 << 16 | 0x29 << 8 | 0x29 ;   // \4 \0 ) )
+
+        // second part of composite type (for data) = bank (tag, num, type ignored, len used)
+        bank[6]  = 12;
+        bank[7]  = 6 << 16 | 0xF << 8 | 1;
+        bank[8]  = 0x2; // N
+        bank[9]  = 0x2; // N
+        bank[10] = 0x00001111;
+        bank[11] = 0x11223344;
+        bank[12] = 0x00002222;
+        bank[13] = 0x55667788;
+
+        bank[14] = 0x2; // N
+        bank[15] = 0x00003333;
+        bank[16] = 0x00991188;
+        bank[17] = 0x00004444;
+        bank[18] = 0x22773366;
+
+        int[] bankData = new int[11];
+        bankData[0]  = 0x2; // N
+        bankData[1]  = 0x2; // N
+        bankData[2]  = 0x00001111;
+        bankData[3]  = 0x11223344;
+        bankData[4]  = 0x00002222;
+        bankData[5]  = 0x55667788;
+
+        bankData[6]  = 0x2; // N
+        bankData[7]  = 0x00003333;
+        bankData[8]  = 0x00991188;    update
+        bankData[9]  = 0x00004444;
+        bankData[10] = 0x22773366;
+        */
+
+        // N(I,D,F,2S,8a)
+        // first part of composite type (for format) = tagseg (tag & type ignored, len used)
+        bank[2]  = 5 << 20 | 0x3 << 16 | 4; // tag = 5, seg has char data, len = 4
+        // ASCII chars values in latest evio string (array) format, N(I,D,F,2S,8a) with N=2
+        bank[3]  = 0x4E << 24 | 0x28 << 16 | 0x49 << 8 | 0x2C;    // N ( I ,
+        bank[4]  = 0x44 << 24 | 0x2C << 16 | 0x46 << 8 | 0x2C;    // D , F ,
+        bank[5]  = 0x32 << 24 | 0x53 << 16 | 0x2C << 8 | 0x38 ;   // 2 S , 8
+        bank[6]  = 0x61 << 24 | 0x29 << 16 | 0x00 << 8 | 0x04 ;   // a ) \0 \4
+
+        // second part of composite type (for data) = bank (tag, num, type ignored, len used)
+        bank[7]  = 16;
+        bank[8]  = 6 << 16 | 0xF << 8 | 1;
+        bank[9]  = 0x2; // N
+        bank[10] = 0x00001111; // I
+        // Double
+        double d = Math.PI * (-1.e-100);
+        long  dl = Double.doubleToLongBits(d);
+        bank[11] = (int) (dl >>> 32);    // higher 32 bits
+        bank[12] = (int)  dl;            // lower 32 bits
+        // Float
+        float f = (float)(Math.PI*(-1.e-24));
+        int  fi = Float.floatToIntBits(f);
+        bank[13] = fi;
+
+        bank[14] = 0x11223344; // 2S
+
+        bank[15]  = 0x48 << 24 | 0x49 << 16 | 0x00 << 8 | 0x48;    // H  I \0  H
+        bank[16]  = 0x4F << 24 | 0x00 << 16 | 0x04 << 8 | 0x04;    // 0 \ 0 \4 \4
+
+        // duplicate data
+        for (int i=0; i < 7; i++) {
+            bank[17+i] = bank[10+i];
+        }
+
+
+        // all composite including headers
+        int[] allData = new int[22];
+        for (int i=0; i < 22; i++) {
+            allData[i] = bank[i+2];
+        }
+
+
+
+        // analyze format string
+        String format = "N(I,D,F,2S,8a)";
+
+        try {
+            // change int array into byte array
+            byte[] byteArray = ByteDataTransformer.toBytes(allData, ByteOrder.BIG_ENDIAN);
+
+            // wrap bytes in ByteBuffer for ease of printing later
+            ByteBuffer buf = ByteBuffer.wrap(byteArray);
+            buf.order(ByteOrder.BIG_ENDIAN);
+
+            // swap
+System.out.println("Call CompositeData.swapAll()");
+            CompositeData.swapAll(byteArray, 0, null, 0, allData.length, ByteOrder.BIG_ENDIAN);
+
+            // print swapped data
+            System.out.println("SWAPPED DATA:");
+            IntBuffer iBuf = buf.asIntBuffer();
+            for (int i=0; i < allData.length; i++) {
+                System.out.println("     0x"+Integer.toHexString(iBuf.get(i)));
+            }
+            System.out.println();
+
+
+
+//            // Create composite object
+//System.out.println("Call CompositeData()");
+//            CompositeData cData2 = new CompositeData(byteArray, ByteOrder.LITTLE_ENDIAN);
+
+//            // print swapped data
+//            System.out.println("After making CompositeData object DATA:");
+//            buf = ByteBuffer.wrap(byteArray);
+//            buf.order(ByteOrder.BIG_ENDIAN);
+//            iBuf = buf.asIntBuffer();
+//            for (int i=0; i < allData.length; i++) {
+//                System.out.println("     0x"+Integer.toHexString(iBuf.get(i)));
+//            }
+//            System.out.println();
+
+
+
+
+
+
+            // swap again
+System.out.println("Call CompositeData.swapAll()");
+            CompositeData.swapAll(byteArray, 0, null, 0, allData.length, ByteOrder.LITTLE_ENDIAN);
+
+            // print double swapped data
+            System.out.println("DOUBLE SWAPPED DATA:");
+            for (int i=0; i < allData.length; i++) {
+                System.out.println("     0x"+Integer.toHexString(iBuf.get(i)));
+            }
+            System.out.println();
+
+            // Create composite object
+            CompositeData cData = new CompositeData(byteArray, ByteOrder.BIG_ENDIAN);
+
+            // print out general data
+            printCompositeDataObject(cData);
+
+            // use alternative method to print out
+            cData.index(0);
+            System.out.println("\nInt    = 0x" + Integer.toHexString(cData.getInt()));
+            System.out.println("Double = " + cData.getDouble());
+            System.out.println("Float  = " + cData.getFloat());
+            System.out.println("Short  = 0x" + Integer.toHexString(cData.getShort()));
+            System.out.println("Short  = 0x" + Integer.toHexString(cData.getShort()));
+            String[] strs = cData.getStrings();
+            for (String s : strs) System.out.println("String = " + s);
+
+            // use toString() method to print out
+            System.out.println("\ntoXmlString =\n" + cData.toXMLString("     ", true));
+
+        }
+        catch (EvioException e) {
+            e.printStackTrace();
+        }
+
+    }
+
+    /** For testing only */
+    public static void main1(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";
+
+        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();
+            System.out.println("EVENT:\n" + ev.toXML());
+        }
+        catch (EvioException e) {
+            e.printStackTrace();
+        }
+        catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+
+    /**
+     * Print the data from a CompositeData object in a user-friendly form.
+     * @param cData CompositeData object
+     */
+    public static void printCompositeDataObject(CompositeData cData) {
+
+        // Get lists of data items & their types from composite data object
+        List<Object> items = cData.getItems();
+        List<DataType> types = cData.getTypes();
+
+        // Use these list to print out data of unknown format
+        DataType type;
+        int len = items.size();
+        for (int i=0; i < len; i++) {
+            type =  types.get(i);
+            System.out.print(String.format("type = %9s, val = ", type));
+            switch (type) {
+                case INT32:
+                case UINT32:
+                case UNKNOWN32:
+                    int j = (Integer)items.get(i);
+                    System.out.println("0x"+Integer.toHexString(j));
+                    break;
+                case LONG64:
+                case ULONG64:
+                    long l = (Long)items.get(i);
+                    System.out.println("0x"+Long.toHexString(l));
+                    break;
+                case SHORT16:
+                case USHORT16:
+                    short s = (Short)items.get(i);
+                    System.out.println("0x"+Integer.toHexString(s));
+                    break;
+                case CHAR8:
+                case UCHAR8:
+                    byte b = (Byte)items.get(i);
+                    System.out.println("0x"+Integer.toHexString(b));
+                    break;
+                case FLOAT32:
+                    float ff = (Float)items.get(i);
+                    System.out.println(""+ff);
+                    break;
+                case DOUBLE64:
+                    double dd = (Double)items.get(i);
+                    System.out.println(""+dd);
+                    break;
+                case CHARSTAR8:
+                    String[] strs = (String[])items.get(i);
+                    for (String ss : strs) {
+                        System.out.print(ss + ", ");
+                    }
+                    System.out.println();
+                    break;
+                default:
+            }
+        }
+
+    }
+
+
+
+}

jevio-base/src/main/java/org/jlab/coda/jevio/test
FileTest.java added at 1.1
diff -N FileTest.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ FileTest.java	28 Feb 2012 19:41:37 -0000	1.1
@@ -0,0 +1,567 @@
+package org.jlab.coda.jevio.test;
+
+import org.jlab.coda.jevio.*;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.List;
+
+/**
+ * Take test programs out of EvioFile.
+ * @author timmer
+ * Date: Oct 7, 2010
+ */
+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);
+
+             eventWriterNew.writeEvent(event);
+
+             // 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);
+             EvioEvent ev = evioReader.parseNextEvent();
+             List<BaseStructure> l = StructureFinder.getMatchingStructures(ev,"seg of banks",
+                                                              NameProviderFactory.createNameProvider(dictionary));
+
+
+             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[]) {
+
+        //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);
+
+        // 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>";
+
+        // 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();
+
+            // 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);
+
+
+
+            // 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);
+
+            eventWriterNew.writeEvent(event);
+
+            // 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);
+            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());
+                }
+            }
+            else {
+                System.out.println("Found NO banks dude");
+            }
+
+        }
+        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);
+
+        // 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>";
+
+        // 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();
+
+            // 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(4, DataType.SHORT16, 4);
+            shortBank2.appendShortData(shortData2);
+            eventBuilder2.addChild(bankBanks, shortBank2);
+
+            eventBuilder2.addChild(eventShort, bankBanks);
+            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);
+
+            eventWriterNew.writeEvent(event);
+
+            // 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[]) {
+
+        //create an event writer to write out the test events.
+        String fileName  = "../../testdata/BigOut2.ev";
+        File file = new File(fileName);
+
+        ByteBuffer myBuf = ByteBuffer.allocate(800);
+        //myBuf.order(ByteOrder.LITTLE_ENDIAN);
+
+        // 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>";
+
+        // 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};
+
+        EvioEvent event = null;
+
+        try {
+            //EventWriter eventWriterNew = new EventWriter(myBuf, 100, 3, dictionary, null);
+            EventWriter eventWriterNew = new EventWriter(file, 1000, 3, ByteOrder.BIG_ENDIAN, dictionary, null);
+
+            // event - bank of banks
+            EventBuilder eventBuilder2 = new EventBuilder(1, DataType.BANK, 1);
+            event = eventBuilder2.getEvent();
+
+            // 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(event, bankBanks);
+            eventWriterNew.writeEvent(event);
+
+            // all done writing
+            eventWriterNew.close();
+        }
+        catch (IOException e) {
+            e.printStackTrace();
+        }
+        catch (EvioException e) {
+            e.printStackTrace();
+        }
+
+
+//        // parse bytes
+//        class myListener implements IEvioListener {
+//
+//            public void startEventParse(EvioEvent evioEvent) { }
+//
+//            public void endEventParse(EvioEvent evioEvent) { }
+//
+//            public void gotStructure(EvioEvent evioEvent, IEvioStructure structure) {
+//
+//                BaseStructureHeader header = structure.getHeader();
+//
+//                System.out.println("------------------");
+//                System.out.println("" + structure);
+//
+//                switch (header.getDataTypeEnum()) {
+//                    case FLOAT32:
+//                        System.out.println("        FLOAT VALS");
+//                        float floatdata[] = structure.getFloatData();
+//                        for (float f : floatdata) {
+//                            System.out.println("         " + f);
+//                        }
+//                        break;
+//
+//                    case DOUBLE64:
+//                        System.out.println("        DOUBLE VALS");
+//                        double doubledata[] = structure.getDoubleData();
+//                        for (double d : doubledata) {
+//                            System.out.println("         " + d);
+//                        }
+//                        break;
+//
+//                    case SHORT16:
+//                        System.out.println("        SHORT VALS");
+//                        short shortdata[] = structure.getShortData();
+//                        for (short i : shortdata) {
+//                            System.out.println("        0x" + Integer.toHexString(i));
+//                        }
+//                        break;
+//
+//                    case INT32:
+//                    case UINT32:
+//                        System.out.println("        INT VALS");
+//                        int intdata[] = structure.getIntData();
+//                        for (int i : intdata) {
+//                            System.out.println("        0x" + Integer.toHexString(i));
+//                        }
+//                        break;
+//
+//                    case LONG64:
+//                        System.out.println("        LONG VALS");
+//                        long longdata[] = structure.getLongData();
+//                        for (long i : longdata) {
+//                            System.out.println("        0x" + Long.toHexString(i));
+//                        }
+//                        break;
+//
+//                    case CHAR8:
+//                    case UCHAR8:
+//                        System.out.println("        BYTE VALS");
+//                        byte bytedata[] = structure.getByteData();
+//                        for (byte i : bytedata) {
+//                            System.out.println("         " + i);
+//                        }
+//                        break;
+//
+//                    case CHARSTAR8:
+//                        System.out.println("        STRING VALS");
+//                        String stringdata[] = structure.getStringData();
+//                        for (String str : stringdata) {
+//                            System.out.println("         " + str);
+//                        }
+//                        break;
+//                }
+//            }
+//        }
+
+
+         // print event
+        System.out.println("Event = \n" + event.toXML());
+
+         //now read it back in
+
+         File fileIn = new File(fileName);
+         System.out.println("read ev file: " + fileName + " size: " + fileIn.length());
+
+         try {
+             EvioReader fileReader = new EvioReader(fileName);
+             System.out.println("\nnum ev = " + fileReader.getEventCount());
+             System.out.println("dictionary = " + fileReader.getDictionaryXML() + "\n");
+
+
+
+             event = fileReader.parseNextEvent();
+             if (event == null) {
+                 System.out.println("We got a NULL event !!!");
+                 return;
+             }
+             System.out.println("Event = \n" + event.toXML());
+             while ( (event = fileReader.parseNextEvent()) != null) {
+                System.out.println("Event = " + event.toString());
+             }
+         }
+         catch (IOException e) {
+             e.printStackTrace();
+         }
+         catch (EvioException e) {
+             e.printStackTrace();
+         }
+     }
+
+}

jevio-base/src/main/java/org/jlab/coda/jevio/test
Tester.java added at 1.1
diff -N Tester.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ Tester.java	28 Feb 2012 19:41:37 -0000	1.1
@@ -0,0 +1,743 @@
+package org.jlab.coda.jevio.test;
+
+import org.jlab.coda.jevio.*;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.DoubleBuffer;
+import java.nio.IntBuffer;
+import java.util.List;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: timmer
+ * Date: 4/12/11
+ * Time: 10:21 AM
+ * To change this template use File | Settings | File Templates.
+ */
+public class Tester {
+
+    private static void printDoubleBuffer(ByteBuffer byteBuffer) {
+        byteBuffer.flip();
+        System.out.println();
+        for (int i=0; i < byteBuffer.limit()/8 ; i++) {
+            System.out.print(byteBuffer.getDouble() + " ");
+            if ((i+1)%8 == 0) System.out.println();
+        }
+        System.out.println();
+    }
+
+    private static void printIntBuffer(ByteBuffer byteBuffer) {
+        byteBuffer.flip();
+        System.out.println();
+        for (int i=0; i < byteBuffer.limit()/4 ; i++) {
+            System.out.print(byteBuffer.getInt() + " ");
+            if ((i+1)%16 == 0) System.out.println();
+        }
+        System.out.println();
+    }
+
+
+    /** For testing only */
+    public static void main(String args[]) {
+
+
+        double freq;
+        long t1, t2, deltaT;
+        final int LOOPS = 1000000;
+
+
+        if (false) {
+
+            double[] doubleData = new double[128];
+            for (int i=0; i < doubleData.length; i++) {
+                doubleData[i] = i;
+            }
+
+            byte[] rawBytesBE = ByteDataTransformer.toBytes(doubleData, ByteOrder.BIG_ENDIAN);
+            byte[] rawBytesLE = ByteDataTransformer.toBytes(doubleData, ByteOrder.LITTLE_ENDIAN);
+            byte[] rawBytes = rawBytesBE;
+
+            ByteBuffer byteBufferBE = ByteBuffer.allocate(1024);
+            ByteBuffer byteBufferLE = ByteBuffer.allocate(1024).order(ByteOrder.LITTLE_ENDIAN);
+            ByteBuffer byteBuffer = byteBufferBE;
+
+            // using only rawBytes as the starting point ...
+
+            // switch byte order or no backing array
+
+            // option 1
+            t1 = System.currentTimeMillis();
+            for (int j=0; j < LOOPS; j++) {
+                byteBuffer.clear();
+                ByteBuffer bbuf = ByteBuffer.wrap(rawBytes).order(ByteOrder.BIG_ENDIAN);
+                int doublesize = rawBytes.length / 8;
+                for (int i = 0; i < doublesize; i++) {
+                    byteBuffer.putDouble(bbuf.getDouble());
+                }
+            }
+            t2 = System.currentTimeMillis();
+            deltaT = t2 - t1; // millisec
+            freq = (double) LOOPS / deltaT * 1000;
+            System.out.printf("loopRate D1: %2.3g Hz,  time = %2.3g sec\n", freq, (deltaT/1000.));
+            //printDoubleBuffer(byteBuffer);
+
+            // option 2
+            t1 = System.currentTimeMillis();
+            for (int j=0; j < LOOPS; j++) {
+                byteBuffer.clear();
+                DoubleBuffer db = byteBuffer.asDoubleBuffer();
+                DoubleBuffer rawBuf = ByteBuffer.wrap(rawBytes).order(ByteOrder.BIG_ENDIAN).asDoubleBuffer();
+                db.put(rawBuf);
+                byteBuffer.position(rawBytes.length);
+            }
+            t2 = System.currentTimeMillis();
+            deltaT = t2 - t1;
+            freq = (double) LOOPS / deltaT * 1000;
+            System.out.printf("loopRate D2: %2.3g Hz,  time = %2.3g sec\n", freq, (deltaT/1000.));
+            //printDoubleBuffer(byteBuffer);
+
+            // keep same endian
+
+
+            // option 3
+            t1 = System.currentTimeMillis();
+            for (int j=0; j < LOOPS; j++) {
+                byteBuffer.clear();
+                System.arraycopy(rawBytes, 0, byteBuffer.array(),
+                                 byteBuffer.position(), rawBytes.length);
+                byteBuffer.position(rawBytes.length);
+            }
+            t2 = System.currentTimeMillis();
+            deltaT = t2 - t1;
+            freq = (double) LOOPS / deltaT * 1000;
+            System.out.printf("loopRate D3: %2.3g Hz,  time = %2.3g sec\n", freq, (deltaT/1000.));
+            //printDoubleBuffer(byteBuffer);
+
+
+            // option 4
+            t1 = System.currentTimeMillis();
+            for (int j=0; j < LOOPS; j++) {
+                byteBuffer.clear();
+                byteBuffer.put(rawBytes);
+            }
+            t2 = System.currentTimeMillis();
+            deltaT = t2 - t1;
+            freq = (double) LOOPS / deltaT * 1000;
+            System.out.printf("loopRate D4: %2.3g Hz,  time = %2.3g sec\n", freq, (deltaT/1000.));
+            //printDoubleBuffer(byteBuffer);
+
+
+            // Using doubleData as the starting point ...
+
+            // option 5
+            t1 = System.currentTimeMillis();
+            for (int j=0; j < LOOPS; j++) {
+                byteBuffer.clear();
+                DoubleBuffer db = byteBuffer.asDoubleBuffer();
+                db.put(doubleData, 0, doubleData.length);
+                byteBuffer.position(8*doubleData.length);
+            }
+            t2 = System.currentTimeMillis();
+            deltaT = t2 - t1;
+            freq = (double) LOOPS / deltaT * 1000;
+            System.out.printf("loopRate D5: %2.3g Hz,  time = %2.3g sec\n", freq, (deltaT/1000.));
+            //printDoubleBuffer(byteBuffer);
+
+
+            // option 6
+            t1 = System.currentTimeMillis();
+            for (int j=0; j < LOOPS; j++) {
+                byteBuffer.clear();
+                for (double value : doubleData) {
+                    byteBuffer.putDouble(value);
+                }
+            }
+            t2 = System.currentTimeMillis();
+            deltaT = t2 - t1;
+            freq = (double) LOOPS / deltaT * 1000;
+            System.out.printf("loopRate D6: %2.3g Hz,  time = %2.3g sec\n", freq, (deltaT/1000.));
+            //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);
+            }
+            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
+
+            try {
+                byte[] bArray = new byte[8*doubleData.length];
+                t1 = System.currentTimeMillis();
+                for (int j=0; j < LOOPS; j++) {
+                    byteBuffer.clear();
+                    ByteDataTransformer.toBytes(doubleData, byteBuffer.order(), bArray, 0);
+                    byteBuffer.put(bArray);
+                }
+                t2 = System.currentTimeMillis();
+                deltaT = t2 - t1;
+                freq = (double) LOOPS / deltaT * 1000;
+                System.out.printf("loopRate D8: %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.
+            }
+
+            // option 9
+
+            try {
+                byte[] bArray = new byte[8*doubleData.length];
+                t1 = System.currentTimeMillis();
+                for (int j=0; j < LOOPS; j++) {
+                    byteBuffer.clear();
+                    ByteDataTransformer.toBytes2(doubleData, byteBuffer.order(), bArray, 0);
+                    byteBuffer.put(bArray);
+                }
+                t2 = System.currentTimeMillis();
+                deltaT = t2 - t1;
+                freq = (double) LOOPS / deltaT * 1000;
+                System.out.printf("loopRate D8: %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.
+            }
+
+        }
+
+
+        if (false) {
+
+            int[] intData = new int[256];
+            for (int i=0; i < intData.length; i++) {
+                intData[i] = i;
+            }
+
+            byte[] rawBytesIBE = ByteDataTransformer.toBytes(intData, ByteOrder.BIG_ENDIAN);
+            byte[] rawBytesILE = ByteDataTransformer.toBytes(intData, ByteOrder.LITTLE_ENDIAN);
+            byte[] rawBytesI = rawBytesIBE;
+
+            ByteBuffer byteBufferIBE = ByteBuffer.allocate(1024);
+            ByteBuffer byteBufferILE = ByteBuffer.allocate(1024).order(ByteOrder.LITTLE_ENDIAN);
+            ByteBuffer byteBufferI = byteBufferIBE;
+
+            // using only rawBytes as the starting point ...
+
+            // switch byte order or no backing array
+
+            // option 1
+            t1 = System.currentTimeMillis();
+            for (int j=0; j < LOOPS; j++) {
+                byteBufferI.clear();
+                ByteBuffer bbuf = ByteBuffer.wrap(rawBytesI).order(ByteOrder.BIG_ENDIAN);
+                int size = rawBytesI.length / 4;
+                for (int i = 0; i < size; i++) {
+                    byteBufferI.putInt(bbuf.getInt());
+                }
+            }
+            t2 = System.currentTimeMillis();
+            deltaT = t2 - t1; // millisec
+            freq = (double) LOOPS / deltaT * 1000;
+            System.out.printf("loopRate I1: %2.3g Hz,  time = %2.3g sec\n", freq, (deltaT/1000.));
+            //printIntBuffer(byteBufferI);
+
+            // option 2
+            t1 = System.currentTimeMillis();
+            for (int j=0; j < LOOPS; j++) {
+                byteBufferI.clear();
+                IntBuffer db = byteBufferI.asIntBuffer();
+                IntBuffer rawBuf = ByteBuffer.wrap(rawBytesI).order(ByteOrder.BIG_ENDIAN).asIntBuffer();
+                db.put(rawBuf);
+                byteBufferI.position(4*db.position());
+            }
+            t2 = System.currentTimeMillis();
+            deltaT = t2 - t1;
+            freq = (double) LOOPS / deltaT * 1000;
+            System.out.printf("loopRate I2: %2.3g Hz,  time = %2.3g sec\n", freq, (deltaT/1000.));
+            //printIntBuffer(byteBufferI);
+
+            // keep same endian
+
+
+            // option 3
+            t1 = System.currentTimeMillis();
+            for (int j=0; j < LOOPS; j++) {
+                byteBufferI.clear();
+                System.arraycopy(rawBytesI, 0, byteBufferI.array(),
+                                 byteBufferI.position(), rawBytesI.length);
+                byteBufferI.position(rawBytesI.length);
+            }
+            t2 = System.currentTimeMillis();
+            deltaT = t2 - t1;
+            freq = (double) LOOPS / deltaT * 1000;
+            System.out.printf("loopRate I3: %2.3g Hz,  time = %2.3g sec\n", freq, (deltaT/1000.));
+            //printIntBuffer(byteBufferI);
+
+
+            // option 4
+            t1 = System.currentTimeMillis();
+            for (int j=0; j < LOOPS; j++) {
+                byteBufferI.clear();
+                byteBufferI.put(rawBytesI);
+            }
+            t2 = System.currentTimeMillis();
+            deltaT = t2 - t1;
+            freq = (double) LOOPS / deltaT * 1000;
+            System.out.printf("loopRate I4: %2.3g Hz,  time = %2.3g sec\n", freq, (deltaT/1000.));
+            //printIntBuffer(byteBufferI);
+
+
+            // Using doubleData as the starting point ...
+
+            // option 5
+            t1 = System.currentTimeMillis();
+            for (int j=0; j < LOOPS; j++) {
+                byteBufferI.clear();
+                IntBuffer db = byteBufferI.asIntBuffer();
+                db.put(intData, 0, intData.length);
+                byteBufferI.position(4*db.position());
+            }
+            t2 = System.currentTimeMillis();
+            deltaT = t2 - t1;
+            freq = (double) LOOPS / deltaT * 1000;
+            System.out.printf("loopRate I5: %2.3g Hz,  time = %2.3g sec\n", freq, (deltaT/1000.));
+            //printIntBuffer(byteBufferI);
+
+
+            // option 6
+            t1 = System.currentTimeMillis();
+            for (int j=0; j < LOOPS; j++) {
+                byteBufferI.clear();
+                for (int value : intData) {
+                    byteBufferI.putInt(value);
+                }
+            }
+            t2 = System.currentTimeMillis();
+            deltaT = t2 - t1;
+            freq = (double) LOOPS / deltaT * 1000;
+            System.out.printf("loopRate I6: %2.3g Hz,  time = %2.3g sec\n", freq, (deltaT/1000.));
+            //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);
+            }
+            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();
+//            for (int j=0; j < LOOPS; j++) {
+//                byteBufferI.clear();
+//                byte[] bytes = ByteDataTransformer.toBytesStream(intData, byteBufferI.order());
+//                byteBufferI.put(bytes);
+//            }
+//            t2 = System.currentTimeMillis();
+//            deltaT = t2 - t1;
+//            freq = (double) LOOPS / deltaT * 1000;
+//            System.out.printf("loopRate I8: %2.3g Hz,  time = %2.3g sec\n", freq, (deltaT/1000.));
+            //printIntBuffer(byteBufferI);
+        }
+
+
+        if (false) {
+
+            try {
+
+                // data
+                int[]    ia = new int[100];
+                double[] da = new double[50];
+
+                EventBuilder eb = new EventBuilder(1, DataType.BANK, 1);
+                // event - bank of banks
+                EvioEvent ev = eb.getEvent();
+
+
+                // bank of ints
+                EvioBank ibanks = new EvioBank(3, DataType.INT32, 3);
+                t1 = System.currentTimeMillis();
+                for (int j=0; j < 2*LOOPS; j++) {
+                    ibanks.appendIntData(ia);
+                    eb.clearData(ibanks);
+                }
+                t2 = System.currentTimeMillis();
+                deltaT = t2 - t1;
+                freq = (double) LOOPS / deltaT * 1000;
+                System.out.printf("loopRate I1: %2.3g Hz,  time = %2.3g sec\n", freq, (deltaT/1000.));
+
+                // bank of double
+                EvioBank dbanks = new EvioBank(3, DataType.DOUBLE64, 3);
+                t1 = System.currentTimeMillis();
+                for (int j=0; j < 2*LOOPS; j++) {
+                    dbanks.appendDoubleData(da);
+                    eb.clearData(dbanks);
+                }
+                t2 = System.currentTimeMillis();
+                deltaT = t2 - t1;
+                freq = (double) LOOPS / deltaT * 1000;
+                System.out.printf("loopRate I2: %2.3g Hz,  time = %2.3g sec\n", freq, (deltaT/1000.));
+
+
+                // Evio event with bank of double data
+                ev = new EvioEvent(1, DataType.BANK, 1);
+                eb.setEvent(ev);
+                dbanks = new EvioBank(3, DataType.DOUBLE64, 3);
+                dbanks.appendDoubleData(da);
+                eb.addChild(ev, dbanks);
+
+                EventWriter evWriter = new EventWriter(ByteBuffer.allocate(32), 550000, 200, null, null);
+                evWriter.close();
+
+                ByteBuffer buffer = ByteBuffer.allocate(4000);
+
+                t1 = System.currentTimeMillis();
+                for (int j=0; j < 2*LOOPS; j++) {
+                    buffer.clear();
+                    evWriter.setBuffer(buffer);
+                    evWriter.writeEvent(ev);
+                    evWriter.close();
+                }
+                t2 = System.currentTimeMillis();
+                deltaT = t2 - t1;
+                freq = (double) LOOPS / deltaT * 1000;
+                System.out.printf("loopRate I3: %2.3g Hz,  time = %2.3g sec\n", freq, (deltaT/1000.));
+
+                // Evio event with bank of int data
+                ev = new EvioEvent(1, DataType.BANK, 1);
+                eb.setEvent(ev);
+                ibanks = new EvioBank(3, DataType.INT32, 3);
+                ibanks.appendIntData(ia);
+                eb.addChild(ev, ibanks);
+
+
+                t1 = System.currentTimeMillis();
+                for (int j=0; j < 2*LOOPS; j++) {
+                    buffer.clear();
+                    evWriter.setBuffer(buffer);
+                    evWriter.writeEvent(ev);
+                    evWriter.close();
+                }
+                t2 = System.currentTimeMillis();
+                deltaT = t2 - t1;
+                freq = (double) LOOPS / deltaT * 1000;
+                System.out.printf("loopRate I4: %2.3g Hz,  time = %2.3g sec\n", freq, (deltaT/1000.));
+
+            }
+            catch (IOException e) {
+                e.printStackTrace();
+            }
+            catch (EvioException e) {
+                e.printStackTrace();
+            }
+
+
+
+
+
+        }
+
+
+        // Test for appendIntData bug ... in which an int array with only
+        // rawData representation is added to. I'm thinking it overwrites
+        // what is there.
+
+        if (false) {
+
+            try {
+                // data
+                int[]    ia1 = new int[] {1,2,3,4,5};
+                int[]    ia2 = new int[] {6,7,8,9,10};
+                double[] da = new double[] {1.,2.,3.,4.,5.};
+
+                EventBuilder eb = new EventBuilder(1, DataType.BANK, 1);
+                // event - bank of banks
+                EvioEvent ev = eb.getEvent();
+
+                // bank of ints
+                EvioBank ibanks = new EvioBank(3, DataType.INT32, 3);
+                ibanks.appendIntData(ia1);
+                eb.addChild(ev, ibanks);
+                eb.setAllHeaderLengths();
+
+                // bank of double
+//                EvioBank dbanks = new EvioBank(3, DataType.DOUBLE64, 3);
+//                dbanks.appendDoubleData(da);
+//                eb.addChild(ev, dbanks);
+
+                // write to file
+                String file  = "/tmp/out.ev";
+                EventWriter eventWriter = new EventWriter(new File(file));
+                eventWriter.writeEvent(ev);
+                eventWriter.close();
+
+                // read in and print
+                System.out.println("Read in from file & print");
+                EvioReader fReader = new EvioReader(file);
+                EvioEvent evR = fReader.parseNextEvent();
+                EvioBank bank = (EvioBank) evR.getChildAt(0);
+                int [] iData = bank.getIntData();
+                for (int i : iData) {
+                    System.out.println(""+i);
+                }
+
+                // read in, add, and print
+                System.out.println("Read in from file, add more, & print");
+                fReader = new EvioReader(file);
+                evR = fReader.parseNextEvent();
+                bank = (EvioBank) evR.getChildAt(0);
+                bank.appendIntData(ia2);
+                iData = bank.getIntData();
+                for (int i : iData) {
+                    System.out.println(""+i);
+                }
+
+            }
+            catch (IOException e) {
+                e.printStackTrace();
+            }
+            catch (EvioException e) {
+                e.printStackTrace();
+            }
+
+        }
+
+
+        // Test for handling short arrays & padding correctly.
+
+        if (true) {
+
+            try {
+                // data
+                byte[]   ba1 = new byte[]  {1,2,3,4,5,6,7};
+                byte[]   ba2 = new byte[]  {8,9,10,11,12,13,14};
+                short[]  sa1 = new short[] {1,2,3,4,5,6,7};
+                short[]  sa2 = new short[] {8,9,10,11,12,13,14};
+                int[]    ia1 = new int[]   {1,2,3,4,5};
+                int[]    ia2 = new int[]   {6,7,8,9,10};
+                long[]   la1 = new long[]  {1,2,3,4,5};
+                long[]   la2 = new long[]  {6,7,8,9,10};
+                float[]  fa1 = new float[] {1,2,3,4,5};
+                float[]  fa2 = new float[] {6,7,8,9,10};
+                double[] da1 = new double[] {1.,2.,3.,4.,5.};
+                double[] da2 = new double[] {6.,7.,8.,9.,10.};
+
+                EventBuilder eb = new EventBuilder(1, DataType.BANK, 1);
+                // event - bank of banks
+                EvioEvent ev = eb.getEvent();
+
+                // bank of ints
+                EvioBank ibanks = new EvioBank(3, DataType.CHARSTAR8, 3);
+                ibanks.appendStringData("string 1"); // len + null = 8
+                eb.addChild(ev, ibanks);
+                eb.setAllHeaderLengths();
+
+
+                // write to file
+                String file  = "/tmp/out.ev";
+                EventWriter eventWriter = new EventWriter(new File(file));
+                eventWriter.writeEvent(ev);
+                eventWriter.close();
+
+                // read in and print
+                System.out.println("Read in from file & print");
+                EvioReader fReader = new EvioReader(file);
+                EvioEvent evR = fReader.parseNextEvent();
+                EvioBank bank = (EvioBank) evR.getChildAt(0);
+                String [] sData = bank.getStringData();
+                for (String i : sData) {
+                    System.out.println(""+i);
+                }
+
+                // read in, add, and print
+                System.out.println("Read in from file, add more, & print");
+                fReader = new EvioReader(file);
+                evR = fReader.parseNextEvent();
+                bank = (EvioBank) evR.getChildAt(0);
+                bank.appendStringData("string 2");
+                bank.appendStringData("string 3");
+                sData = bank.getStringData();
+                for (String i : sData) {
+                    System.out.println(""+i);
+                }
+
+            }
+            catch (IOException e) {
+                e.printStackTrace();
+            }
+            catch (EvioException e) {
+                e.printStackTrace();
+            }
+
+        }
+
+        // test to see which way is best to convert byte array to int array
+
+        if (false) {
+
+            int[] intData = new int[256];
+            for (int i=0; i < intData.length; i++) {
+                intData[i] = i;
+            }
+
+            double[] doubleData = new double[256];
+            for (int i=0; i < doubleData.length; i++) {
+                doubleData[i] = i;
+            }
+
+            byte[] rawBytesIBE = ByteDataTransformer.toBytes(intData, ByteOrder.BIG_ENDIAN);
+            byte[] rawBytesILE = ByteDataTransformer.toBytes(intData, ByteOrder.LITTLE_ENDIAN);
+            byte[] rawBytesI = rawBytesILE;
+
+
+            // option 1 (toIntArray)
+            t1 = System.currentTimeMillis();
+            for (int j=0; j < LOOPS; j++) {
+                int indx;
+                int[] ints = new int[rawBytesI.length / 4];
+                for (int i = 0; i < ints.length; i++) {
+                    indx = i*4;
+                    ints[i] = ByteDataTransformer.toInt(rawBytesI[indx],
+                                    rawBytesI[indx+1],
+                                    rawBytesI[indx+2],
+                                    rawBytesI[indx+3],
+                                    ByteOrder.LITTLE_ENDIAN);
+                }
+            }
+            t2 = System.currentTimeMillis();
+            deltaT = t2 - t1; // millisec
+            freq = (double) LOOPS / deltaT * 1000;
+            System.out.printf("loopRate I1: %2.3g Hz,  time = %2.3g sec\n", freq, (deltaT/1000.));
+
+
+            // option 2 (toIntArray)
+            t1 = System.currentTimeMillis();
+            int[] ii = new int[rawBytesI.length / 4];
+            for (int j=0; j < LOOPS; j++) {
+                for (int i = 0; i < rawBytesI.length-3; i+=4) {
+                    ii[i/4+0] = ByteDataTransformer.toInt(rawBytesI[i],
+                                          rawBytesI[i+1],
+                                          rawBytesI[i+2],
+                                          rawBytesI[i+3],
+                                          ByteOrder.LITTLE_ENDIAN);
+                }
+            }
+            t2 = System.currentTimeMillis();
+            deltaT = t2 - t1; // millisec
+            freq = (double) LOOPS / deltaT * 1000;
+            System.out.printf("loopRate I2: %2.3g Hz,  time = %2.3g sec\n", freq, (deltaT/1000.));
+
+
+            // option 3 (getAsIntArray)
+            t1 = System.currentTimeMillis();
+            for (int j=0; j < LOOPS; j++) {
+                ByteBuffer byteBuffer = ByteBuffer.wrap(rawBytesI).order(ByteOrder.LITTLE_ENDIAN);
+                int intsize = rawBytesI.length / 4;
+                int array[] = new int[intsize];
+                for (int i = 0; i < intsize; i++) {
+                    array[i] = byteBuffer.getInt();
+                }
+            }
+            t2 = System.currentTimeMillis();
+            deltaT = t2 - t1;
+            freq = (double) LOOPS / deltaT * 1000;
+            System.out.printf("loopRate I3: %2.3g Hz,  time = %2.3g sec\n", freq, (deltaT/1000.));
+            //printIntBuffer(byteBufferI);
+
+            // option 3 (getAsIntArray2)
+            t1 = System.currentTimeMillis();
+            for (int j=0; j < LOOPS; j++) {
+                ByteBuffer byteBuffer = ByteBuffer.wrap(rawBytesI).order(ByteOrder.LITTLE_ENDIAN);
+                IntBuffer ibuf = IntBuffer.wrap(new int[rawBytesI.length/4]);
+                byteBuffer.asIntBuffer().put(ibuf);
+            }
+            t2 = System.currentTimeMillis();
+            deltaT = t2 - t1;
+            freq = (double) LOOPS / deltaT * 1000;
+            System.out.printf("loopRate I4: %2.3g Hz,  time = %2.3g sec\n", freq, (deltaT/1000.));
+            //printIntBuffer(byteBufferI);
+
+
+            // option 4 (toDoubleArray)
+            t1 = System.currentTimeMillis();
+            try {
+                for (int j=0; j < LOOPS; j++) {
+                    ByteDataTransformer.toDoubleArray(rawBytesI, ByteOrder.BIG_ENDIAN);
+                }
+            }
+            catch (EvioException e) {
+                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
+            }
+            t2 = System.currentTimeMillis();
+            deltaT = t2 - t1; // millisec
+            freq = (double) LOOPS / deltaT * 1000;
+            System.out.printf("loopRate D1: %2.3g Hz,  time = %2.3g sec\n", freq, (deltaT/1000.));
+
+
+            // option 5 (getAsDoubleArray)
+            t1 = System.currentTimeMillis();
+            for (int j=0; j < LOOPS; j++) {
+                ByteDataTransformer.getAsDoubleArray(rawBytesI, ByteOrder.BIG_ENDIAN);
+            }
+            t2 = System.currentTimeMillis();
+            deltaT = t2 - t1;
+            freq = (double) LOOPS / deltaT * 1000;
+            System.out.printf("loopRate D2: %2.3g Hz,  time = %2.3g sec\n", freq, (deltaT/1000.));
+            //printIntBuffer(byteBufferI);
+
+
+        }
+
+//        // data
+//        short[] shortData1 = new short[] {1,2,3};
+//
+//        try {
+//
+//            // event - bank of banks
+//            EventBuilder eventBuilder = new EventBuilder(1, DataType.SEGMENT, 1);
+//            EvioEvent eventShort = eventBuilder.getEvent();
+//
+//            // seg of banks
+//            EvioSegment segBanks = new EvioSegment(2, DataType.BANK);
+//
+//            // bank has 3 shorts
+//            EvioBank shortBank1 = new EvioBank(3, DataType.SHORT16, 3);
+//            shortBank1.appendShortData(shortData1);
+//
+//            // add short bank to seg of banks
+//            eventBuilder.addChild(segBanks, shortBank1);
+//
+//            // add seg of banks to event
+//            eventBuilder.addChild(eventShort, segBanks);
+//
+//            StructureTransformer st = new StructureTransformer();
+//            EvioBank bank = st.transform(segBanks, 55);
+//
+//            System.out.println("Hey we made it.");
+//        }
+//        catch (EvioException e) {
+//            e.printStackTrace();
+//        }
+//
+
+    }
+
+
+
+}
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