14 added + 31 modified, total 45 files
jevio-base/src/main/java/org/jlab/coda/jevio
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
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
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
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
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
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
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<Type>() 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<Type>() 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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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> 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
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
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
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
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
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
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
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
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
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
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
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