adding sources for evio 4
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; + } + +}
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]
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(); + } + + +}
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
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
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]
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]
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; + } + } +}
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; + } + + } + +}
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()); + } + +}
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; + } + +}
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; + } + +}
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]
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; + } + + +}
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); + } + } + +}
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; + } + +}
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); + } + +}
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; + } + +}
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 + +}
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]
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; + } + +}
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; + } + +}
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(); + } +}
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(); +}
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); +}
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); + +}
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); +}
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(); + + + +}
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); +}
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); +}
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); + } +}
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; + } + +}
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
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); + } + +}
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; + } +}
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; + } +}
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
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()); + } + +}
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; +}
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
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
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
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: + } + } + + } + + + +}
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(); + } + } + +}
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(); +// } +// + + } + + + +}
Use REPLY-ALL to reply to list
To unsubscribe from the LCD-CVS list, click the following link:
https://listserv.slac.stanford.edu/cgi-bin/wa?SUBED1=LCD-CVS&A=1