/* * Java class "BinaryArrayImpl.java" generated from Poseidon for UML. * Poseidon for UML is developed by <A HREF="http://www.gentleware.com">Gentleware</A>. * Generated with <A HREF="http://jakarta.apache.org/velocity/">velocity</A> template engine. */ package com.compomics.util; import org.apache.xerces.impl.dv.util.Base64; import java.nio.ByteBuffer; import java.nio.ByteOrder; /** * <p>Persistence independent object model implementation class:</p> * <p>Implements a binary array of the kind found in * mzData/spectrumList/spectrum/mzArrayBinary or * mzData/spectrumList/spectrum/intenArrayBinary.  Holds both the data * itself and meta data describing the encoding of the data.</p> * * @author Philip Jones */ public class BinaryArrayImpl { /** * Defines the valid String indicating big endian byte order. */ public static final String BIG_ENDIAN_LABEL = "big"; /** * Defines the valid String indicating little endian byte order. */ public static final String LITTLE_ENDIAN_LABEL = "little"; /** * Defines the valid String indicating the correct precision for encoded * floats. */ public static final String FLOAT_PRECISION = "32"; /** * Defines the valid String indicating the correct precision for encoded * doubles. */ public static final String DOUBLE_PRECISION = "64"; /** * Defines the number of bytes required in an UNENCODED byte array to hold a * single float value. */ public static final int BYTES_TO_HOLD_FLOAT = 4; /** * Defines the number of bytes required in an UNENCODED byte array to hold a * dingle double value. */ public static final int BYTES_TO_HOLD_DOUBLE = 8; /////////////////////////////////////// // attributes /** * <p>Represents the binary contents of the array as an array of bytes. * (mzData element .../binaryDataGroup.)</p> <p>Note that the contents of * this field can be obtained from a database by calling the * <code>java.sql.Blob.getBytes():byte[]</code> method.</p> */ protected String iBase64String = null; /** * <p>Represents the length of the binary array (mzData element * .../data/length).</p> */ protected long iDataLength = -1; /** * <p>Represents the endian value of the binary array (mzData element * .../data/endian).</p> */ protected String iDataEndian = null; /** * <p>Represents the precision of the binary array (mzData element * .../data/precision).</p> */ protected String iDataPrecision = null; /////////////////////////////////////// // operations /** * <p>Default constructor with protected access.</p> */ protected BinaryArrayImpl() { } // end BinaryArrayImpl /** * <p>Creates an instance of this BinaryArray object, setting all fields as * per description below.</p> * * @param aBase64String the binary contents of the array as an array of * bytes. (mzData element .../binaryDataGroup.) Note that the contents of * this field can be obtained from a database by calling the * java.sql.Blob.getBytes():byte[] method. * @param aDataLength the length of the binary array (mzData element * .../data/length). * @param aDataEndian The byte order is used when reading or writing * multibyte values stored as mzData element .../data/endian. Only possible * values are defined by the static String members of this class * 'BIG_ENDIAN_LABEL' (or "big") and 'LITTLE_ENDIAN_LABEL' (or "little"). * @param aDataPrecision the precision of the binary array (mzData element * .../data/precision) that indicates if the array contains encoded double * values or encoded float values. Only possible values for this parameter * are defined byt he static String members of this class 'FLOAT_PRECISION' * (or "32") and 'DOUBLE_PRECISION' (or "64"). */ public BinaryArrayImpl(String aBase64String, long aDataLength, String aDataEndian, String aDataPrecision) { this.iDataEndian = aDataEndian; constructorCommon(aBase64String, aDataLength, aDataPrecision); } // end BinaryArrayImpl /** * Constructor that allows the creation of a BinaryArray object using an * array of double values. * * @param doubleArray being the array of double values to be converted to * base64 and placed in the mzData element .../binaryDataGroup. * @param aDataEndian The byte order is used when reading or writing * multibyte values stored as mzData element .../data/endian. Only possible * values are defined by the static String members of this class * 'BIG_ENDIAN_LABEL' (or "big") and 'LITTLE_ENDIAN_LABEL' (or "little"). */ public BinaryArrayImpl(double[] doubleArray, String aDataEndian) { this.iDataEndian = aDataEndian; if (doubleArray == null) { throw new IllegalArgumentException("The double[] 'doubleArray' that has been passed into the method BinaryArrayImpl is null. This is not valid."); } ByteBuffer buffer = ByteBuffer.allocate(doubleArray.length * BYTES_TO_HOLD_DOUBLE); buffer.order(getByteOrder()); for (double aDoubleArray : doubleArray) { buffer.putDouble(aDoubleArray); } String base64String = Base64.encode(buffer.array()); constructorCommon(base64String, doubleArray.length, DOUBLE_PRECISION); } /** * Constructor that allows the creation of a BinaryArray object using an * array of float values. * * @param floatArray being the array of float values to be converted to * base64 and placed in the mzData element .../binaryDataGroup. * @param aDataEndian The byte order is used when reading or writing * multibyte values stored as mzData element .../data/endian. Only possible * values are defined by the static String members of this class * 'BIG_ENDIAN_LABEL' (or "big") and 'LITTLE_ENDIAN_LABEL' (or "little"). */ public BinaryArrayImpl(float[] floatArray, String aDataEndian) { this.iDataEndian = aDataEndian; if (floatArray == null) { throw new IllegalArgumentException("The float[] 'floatArray' that has been passed into the BinaryArrayImpl is null. This is not valid."); } ByteBuffer buffer = ByteBuffer.allocate(floatArray.length * BYTES_TO_HOLD_FLOAT); buffer.order(getByteOrder()); for (float aFloatArray : floatArray) { buffer.putFloat(aFloatArray); } String base64String = Base64.encode(buffer.array()); constructorCommon(base64String, floatArray.length, FLOAT_PRECISION); } /** * Private helper to factor out common behaviour of the two constructors * that take arrays of numbers to be encoded. * * @param aBase64String the binary contents of the array as an array of * bytes. * @param aDataLength the length of the binary array (mzData element * .../data/length). stored as mzData element .../data/endian. Only possible * values are defined by the static String members of this class * 'BIG_ENDIAN_LABEL' (or "big") and 'LITTLE_ENDIAN_LABEL' (or "little"). * @param aDataPrecision the precision of the binary array (mzData element * .../data/precision) that indicates if the array contains encoded double * values or encoded float values. Only possible values for this parameter * are defined byt he static String members of this class 'FLOAT_PRECISION' * (or "32") and 'DOUBLE_PRECISION' (or "64"). */ private void constructorCommon(String aBase64String, long aDataLength, String aDataPrecision) { if (aBase64String == null) { throw new IllegalArgumentException("Attempting to instantiate a BinaryArrayImpl without specifying a valid value for aBase64String"); } if (aBase64String.indexOf('\n') > -1) { // Strip out white space if any present. this.iBase64String = aBase64String.trim().replaceAll("\\n", ""); } else { this.iBase64String = aBase64String.trim(); } this.iDataLength = aDataLength; this.iDataPrecision = aDataPrecision; } public String getBase64String() { return iBase64String; } // end getBase64String /** * <p>Returns the contents of the binary array <i>decoded</i> using the * Base64 algorithm.</p> * * @return the contents of the binary array <i>decoded</i> using the Base64 * algorithm. */ public byte[] getDecodedByteArray() { if (this.getBase64String() == null) { return null; } else { return Base64.decode(this.getBase64String()); } } /** * Returns the number of bytes used for each element in the (NON-encoded) * byte array depending on the precision of the data. * * @return an int value being either 4 (for storage of floats) or 8 (for * storage of doubles). */ private int getStep() { if (FLOAT_PRECISION.equals(this.getDataPrecision())) { return BYTES_TO_HOLD_FLOAT; } else if (DOUBLE_PRECISION.equals(this.getDataPrecision())) { return BYTES_TO_HOLD_DOUBLE; } else { throw new IllegalStateException("The value for data precision for this binary array must be either 32 or 64. In this case it is: " + this.getDataPrecision()); } } /** * Returns the appropriate ByteOrder object depending on whether big endian * or little endian byte order is being used. * * @return the appropriate ByteOrder object depending on whether big endian * or little endian byte order is being used. */ private ByteOrder getByteOrder() { if (BIG_ENDIAN_LABEL.equals(this.getDataEndian())) { return ByteOrder.BIG_ENDIAN; } else if (LITTLE_ENDIAN_LABEL.equals(this.getDataEndian())) { return ByteOrder.LITTLE_ENDIAN; } else { throw new IllegalStateException("The value for data endian for this binary array must be either 'big' or 'little'. In this case it is: " + this.getDataPrecision()); } } /** * Checks if all the necessary information is provided and then converts the * decoded binary array into an array of double values (that for example * could be used to draw a spectra). * * @return the decoded binary array converted into an array of double * values. */ public double[] getDoubleArray() { // Note the 'precision' value is constrained to be only "32" or "64". int step = getStep(); byte[] fullArray = this.getDecodedByteArray(); if (fullArray == null || fullArray.length == 0) { // No data array set - return null. return null; } if (fullArray.length % step != 0) { throw new IllegalStateException("Error caused by attempting to split a byte array of length " + fullArray.length + " into pieces of length " + step); } double[] doubleArray = new double[fullArray.length / step]; ByteBuffer bb = ByteBuffer.wrap(fullArray); // Set the order to BIG or LITTLE ENDIAN bb.order(getByteOrder()); for (int indexOut = 0; indexOut < fullArray.length; indexOut += step) { /* * Note that the 'getFloat(index)' method gets the next 4 bytes and * the 'getDouble(index)' method gets the next 8 bytes. */ doubleArray[indexOut / step] = (step == BYTES_TO_HOLD_FLOAT) ? (double) bb.getFloat(indexOut) : bb.getDouble(indexOut); } return doubleArray; } /** * <p>Returns the length of the binary array (mzData element * .../data/length).</p> * * @return Returns the length of the binary array (mzData element * .../data/length). */ public long getDataLength() { return iDataLength; } // end getDataLength /** * <p>Returns the endian value of the binary array (mzData element * .../data/endian).</p> * * @return Returns the endian value of the binary array (mzData element * .../data/endian). */ public String getDataEndian() { return iDataEndian; } // end getDataEndian /** * <p>Returns the precision of the binary array (mzData element * .../data/precision).</p> * * @return Returns the precision of the binary array (mzData element * .../data/precision). */ public String getDataPrecision() { return iDataPrecision; } // end getDataPrecision /** * Returns a useful String representation of this Imlplementation instance * that includes details of all fields. * * @return a useful String representation of this Imlplementation instance. */ public String toString() { final StringBuffer buf = new StringBuffer(); buf.append(" BinaryArrayImpl:").append(" DataLength=").append(iDataLength).append(", DataEndian=").append(iDataEndian).append(", DataPrecision=").append(iDataPrecision).append("\n, Base64 String='").append(iBase64String).append("'"); buf.append("}\n"); if (iBase64String != null) { byte[] decoded = this.getDecodedByteArray(); buf.append("\n...DECODED: "); for (int i = 0; i < decoded.length; ++i) { buf.append(i == 0 ? "" : ", ").append(decoded[i]); } buf.append("}\n"); double[] decodedDouble = this.getDoubleArray(); buf.append("\n...TO DOUBLE ARRAY: "); for (int i = 0; i < decodedDouble.length; i++) { buf.append(i == 0 ? "" : ", ").append(decodedDouble[i]); } buf.append("}\n"); } buf.append("}\n"); return buf.toString(); } /** * <p>Performs equals methods dependent on values of instance variables and * class of Object o.</p> * * @param o Object that this is being compared with. * @return boolean indicating equality of this object with the parameter * Object o */ public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } final BinaryArrayImpl that = (BinaryArrayImpl) o; if (this.getDataLength() != that.getDataLength()) { return false; } if (!this.getDataEndian().equals(that.getDataEndian())) { return false; } if (!this.getDataPrecision().equals(that.getDataPrecision())) { return false; } if (this.getBase64String() != null ? !this.getBase64String().equals(that.getBase64String()) : that.getBase64String() != null) { return false; } return true; } public int hashCode() { int result; result = (this.getBase64String() != null ? this.getBase64String().hashCode() : 0); result = 29 * result + (int) (this.getDataLength() ^ (this.getDataLength() >>> 32)); result = 29 * result + this.getDataEndian().hashCode(); result = 29 * result + this.getDataPrecision().hashCode(); return result; } } // end BinaryArrayImpl