ubic.basecode.io.ByteArrayConverter.java Source code

Java tutorial

Introduction

Here is the source code for ubic.basecode.io.ByteArrayConverter.java

Source

/*
 * The baseCode project
 * 
 * Copyright (c) 2006 University of British Columbia
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
package ubic.basecode.io;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.DoubleBuffer;
import java.nio.IntBuffer;
import java.nio.LongBuffer;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang3.ArrayUtils;

import cern.colt.list.ByteArrayList;
import cern.colt.list.DoubleArrayList;

/**
 * Class to convert byte arrays (e.g., Blobs) to and from other types of arrays. TODO these could be static methods.
 * 
 * @author Kiran Keshav
 * @author Paul Pavlidis
 * 
 */
public final class ByteArrayConverter {

    // sizes are in bytes.

    /**
     * 3.3.4 The boolean Type
     * <p>
     * Although the Java virtual machine defines a boolean type, it only provides very limited support for it. There are
     * no Java virtual machine instructions solely dedicated to operations on boolean values. Instead, expressions in
     * the Java programming language that operate on boolean values are compiled to use values of the Java virtual
     * machine int data type.
     * <p>
     * The Java virtual machine does directly support boolean arrays. Its newarray instruction enables creation of
     * boolean arrays. Arrays of type boolean are accessed and modified using the byte array instructions baload and
     * bastore.2
     * <p>
     * The Java virtual machine encodes boolean array components using 1 to represent true and 0 to represent false.
     * Where Java programming language boolean values are mapped by compilers to values of Java virtual machine type
     * int, the compilers must use the same encoding.
     * 
     * @see http://java.sun.com/docs/books/vmspec/2nd-edition/html/Overview.doc.html#12237
     */
    private static final int BOOL_SIZE = 1; // erm...this seems to work.

    private static final int DOUBLE_SIZE = 8;

    /**
     * @param boolarray
     * @return byte[]
     */
    public byte[] booleanArrayToBytes(boolean[] boolarray) {
        if (boolarray == null)
            return null;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        DataOutputStream dos = new DataOutputStream(bos);
        try {
            for (boolean element : boolarray) {
                dos.writeBoolean(element);
            }
        } catch (IOException e) {
            // do nothing
        }
        return bos.toByteArray();
    }

    /**
     * Convert a byte array with one-byte-per-character ASCII encoding (aka ISO-8859-1).
     * 
     * @param barray
     * @return
     */
    public String byteArrayToAsciiString(byte[] barray) {
        if (barray == null)
            return null;
        try {
            return new String(barray, "ISO-8859-1");
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("Conversion error", e);
        }
    }

    /**
     * @param barray
     * @return boolean[]
     */
    public boolean[] byteArrayToBooleans(byte[] barray) {
        if (barray == null)
            return null;
        ByteArrayInputStream bis = new ByteArrayInputStream(barray);
        DataInputStream dis = new DataInputStream(bis);
        boolean[] iarray = new boolean[barray.length / BOOL_SIZE];
        int i = 0;

        try {
            while (dis.available() > 0) {
                iarray[i] = dis.readBoolean();
                i++;
            }
            return iarray;

        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                dis.close();
                bis.close();

            } catch (IOException e) {
                throw new RuntimeException(e);

            }
        }

    }

    /**
     * @param barray
     * @return char[]
     */
    public char[] byteArrayToChars(byte[] barray) {
        if (barray == null)
            return null;

        CharBuffer buf = ByteBuffer.wrap(barray).asCharBuffer();
        char[] array = new char[buf.remaining()];
        buf.get(array);
        return array;

    }

    /**
     * @param barray
     * @param width how many items per row.
     * @return double[][]
     */
    public double[][] byteArrayToDoubleMatrix(byte[] barray, int width) throws IllegalArgumentException {

        int numDoubles = barray.length / DOUBLE_SIZE;
        if (numDoubles % width != 0) {
            throw new IllegalArgumentException("The number of doubles in the byte array (" + numDoubles
                    + ") does not divide evenly into the number of items expected per row (" + width + ").");
        }

        int numRows = numDoubles / width;

        double[][] answer = new double[numRows][];

        byte[] row = new byte[width * DOUBLE_SIZE];
        int bytesPerRow = width * DOUBLE_SIZE;
        for (int rownum = 0; rownum < numRows; rownum++) {

            int offset = rownum * bytesPerRow;
            System.arraycopy(barray, offset, row, 0, bytesPerRow);
            // for ( int i = 0; i < bytesPerRow; i++ ) {
            // row[i] = barray[i + offset];
            // }

            answer[rownum] = byteArrayToDoubles(row);
        }
        return answer;
    }

    /**
     * @param barray
     * @return double[]
     */
    public double[] byteArrayToDoubles(byte[] barray) {
        if (barray == null)
            return null;

        DoubleBuffer buf = ByteBuffer.wrap(barray).asDoubleBuffer();
        double[] array = new double[buf.remaining()];
        buf.get(array);

        return array;

    }

    /**
     * @param barray
     * @return int[]
     */
    public int[] byteArrayToInts(byte[] barray) {
        if (barray == null)
            return null;

        IntBuffer intBuf = ByteBuffer.wrap(barray).asIntBuffer();
        int[] array = new int[intBuf.remaining()];
        intBuf.get(array);

        return array;

    }

    /**
     * @param barray
     * @return long[] resulting from parse of the bytes.
     */
    public long[] byteArrayToLongs(byte[] barray) {
        if (barray == null)
            return null;

        LongBuffer buf = ByteBuffer.wrap(barray).asLongBuffer();
        long[] array = new long[buf.remaining()];
        buf.get(array);

        return array;
    }

    /**
     * Convert a byte array into a array of Strings. It is assumed that separate strings are delimited by '\u0000'
     * (NUL). Note that this method cannot differentiate between empty strings and null strings. A string that is empty
     * will be returned as an empty string, not null.
     * 
     * @param bytes
     * @return
     */
    public String[] byteArrayToStrings(byte[] bytes) {
        List<String> strings = new ArrayList<String>();
        ByteArrayList buf = new ByteArrayList();
        for (byte element : bytes) {
            if (element == '\u0000') {
                String newString = new String(buf.elements());
                newString = newString.trim();
                strings.add(newString);
                buf = new ByteArrayList();
            } else {
                buf.add(element);
            }
        }

        String[] result = new String[strings.size()];
        for (int i = 0; i < strings.size(); i++) {
            result[i] = strings.get(i);
        }
        return result;
    }

    /**
     * Convert a byte array to a tab-delimited string.
     * 
     * @param bytes
     * @param type The Class of primitives the bytes are to be interpreted as. If this is String, then the bytes are
     *        directly interpreted as tab-delimited string (e.g., no extra tabs are added).
     * @return
     * @throws UnsupportedOperationException if Class is a type that can't be converted by this.
     */
    public String byteArrayToTabbedString(byte[] bytes, Class<?> type) {
        if (bytes == null)
            return null;

        if (type.equals(Double.class)) {
            Double[] array = ArrayUtils.toObject(byteArrayToDoubles(bytes));
            return formatAsString(array);
        } else if (type.equals(Integer.class)) {
            Integer[] array = ArrayUtils.toObject(byteArrayToInts(bytes));
            return formatAsString(array);
        } else if (type.equals(Long.class)) {
            Long[] array = ArrayUtils.toObject(byteArrayToLongs(bytes));
            return formatAsString(array);
        } else if (type.equals(String.class)) {
            return byteArrayToAsciiString(bytes);
        } else if (type.equals(Boolean.class)) {
            Boolean[] array = ArrayUtils.toObject(byteArrayToBooleans(bytes));
            return formatAsString(array);
        } else if (type.equals(Character.class)) {
            Character[] array = ArrayUtils.toObject(byteArrayToChars(bytes));
            return formatAsString(array);
        } else {
            throw new UnsupportedOperationException("Can't convert " + type.getName());
        }

    }

    /**
     * @param carray
     * @return byte[]
     */
    public byte[] charArrayToBytes(char[] carray) {
        if (carray == null)
            return null;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        DataOutputStream dos = new DataOutputStream(bos);

        try {
            for (char element : carray) {
                dos.writeChar(element);
            }
            dos.close();
            bos.close();

        } catch (IOException e) {
            // do nothing.
        }

        return bos.toByteArray();
    }

    /**
     * @param darray
     * @return byte[]
     */
    public byte[] doubleArrayToBytes(double[] darray) {
        if (darray == null)
            return null;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        DataOutputStream dos = new DataOutputStream(bos);
        try {
            for (double element : darray) {
                dos.writeDouble(element);
            }
        } catch (IOException e) {
            // do nothing
        }
        return bos.toByteArray();
    }

    /**
     * @param darray
     * @return byte[]
     */
    public byte[] doubleArrayToBytes(Double[] darray) {
        return doubleArrayToBytes(ArrayUtils.toPrimitive(darray));
    }

    /**
     * @param darray
     * @return
     */
    public byte[] doubleArrayToBytes(DoubleArrayList darray) {
        return doubleArrayToBytes((Double[]) darray.toList().toArray(new Double[] {}));
    }

    /**
     * @param testm
     * @return
     */
    public byte[] doubleMatrixToBytes(double[][] testm) {

        if (testm == null || testm.length == 0)
            throw new IllegalArgumentException("Null or empty matrix");

        int rowSize = testm[0].length;

        double[] a = new double[testm.length * rowSize];

        for (int i = 0; i < testm.length; i++) {
            if (testm[i].length != rowSize)
                throw new IllegalArgumentException("Cannot serialize ragged matrix");
            for (int j = 0; j < rowSize; j++) {
                a[j + rowSize * i] = testm[i][j];
            }
        }
        return doubleArrayToBytes(a);

    }

    /**
     * @param iarray
     * @return byte[]
     */
    public byte[] intArrayToBytes(int[] iarray) {
        if (iarray == null)
            return null;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        DataOutputStream dos = new DataOutputStream(bos);
        try {
            for (int element : iarray) {
                dos.writeInt(element);
            }
            dos.close();
            bos.close();
        } catch (IOException e) {
            // do nothing
        }
        return bos.toByteArray();
    }

    /**
     * @param larray
     * @return byte[]
     */
    public byte[] longArrayToBytes(long[] larray) {
        if (larray == null)
            return null;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        DataOutputStream dos = new DataOutputStream(bos);
        try {
            for (long element : larray) {
                dos.writeLong(element);
            }
            dos.close();
            bos.close();
        } catch (IOException e) {
            // do nothing
        }
        return bos.toByteArray();
    }

    /**
     * Note that this method cannot differentiate between empty strings and null strings. A string that is empty will be
     * returned as an empty string, not null, while a null string will be stored as an empty string.
     * 
     * @param stringArray
     * @return byte[]
     */
    public byte[] stringArrayToBytes(Object[] stringArray) {
        if (stringArray == null)
            return null;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        DataOutputStream dos = new DataOutputStream(bos);

        try {
            for (Object element : stringArray) {
                String string = (String) element;
                if (string != null) {
                    dos.write(string.getBytes());
                }
                dos.write('\u0000');
            }
            dos.close();
            bos.close();

        } catch (IOException e) {
            // do nothing.
        }

        return bos.toByteArray();
    }

    /**
     * @param data
     */
    public byte[] toBytes(Object data) {
        return toBytes(new Object[] { data });
    }

    /**
     * Convert an array of Objects into an array of bytes. If the array contains Strings, it is converted to a
     * tab-delimited string, and then converted to bytes.
     * 
     * @param array of Objects to be converted to bytes.
     * @return
     * @throws UnsupportedOperationException if Objects are a type that can't be converted by this.
     */
    public byte[] toBytes(Object[] array) {
        if (array == null)
            return null;
        if (array.length == 0)
            return new byte[] {};

        // sanity check, catches obvious errors.
        if (array[0] == null)
            throw new IllegalArgumentException("Null values cannot be converted");

        if (array[0] instanceof Boolean) {
            boolean[] toConvert = new boolean[array.length];
            for (int i = 0; i < array.length; i++) {
                boolean object = ((Boolean) array[i]).booleanValue();
                toConvert[i] = object;
            }
            return booleanArrayToBytes(toConvert);
        } else if (array[0] instanceof Double) {
            double[] toConvert = new double[array.length];
            for (int i = 0; i < array.length; i++) {
                double object = ((Double) array[i]).doubleValue();
                toConvert[i] = object;
            }
            return doubleArrayToBytes(toConvert);
        } else if (array[0] instanceof Character) {
            char[] toConvert = new char[array.length];
            for (int i = 0; i < array.length; i++) {
                char object = ((Character) array[i]).charValue();
                toConvert[i] = object;
            }
            return charArrayToBytes(toConvert);
        } else if (array[0] instanceof String) {
            return stringArrayToBytes(array);
        } else if (array[0] instanceof Integer) {
            int[] toConvert = new int[array.length];
            for (int i = 0; i < array.length; i++) {
                int object = ((Integer) array[i]).intValue();
                toConvert[i] = object;
            }
            return intArrayToBytes(toConvert);
        } else if (array[0] instanceof Long) {
            long[] toConvert = new long[array.length];
            for (int i = 0; i < array.length; i++) {
                int object = ((Long) array[i]).intValue();
                toConvert[i] = object;
            }
            return longArrayToBytes(toConvert);
        } else {
            throw new UnsupportedOperationException("Can't convert " + array[0].getClass() + " to bytes");
        }

    }

    /**
     * @param array
     * @return
     */
    private String formatAsString(Object[] array) {
        StringBuffer buf = new StringBuffer();
        for (int i = 0; i < array.length; i++) {
            buf.append(array[i]);
            if (i != array.length - 1)
                buf.append("\t"); // so we don't have a trailing tab.
        }
        return buf.toString();
    }
}