gov.nih.nci.caarray.util.CaArrayUtils.java Source code

Java tutorial

Introduction

Here is the source code for gov.nih.nci.caarray.util.CaArrayUtils.java

Source

//======================================================================================
// Copyright 5AM Solutions Inc, Yale University
//
// Distributed under the OSI-approved BSD 3-Clause License.
// See http://ncip.github.com/caarray/LICENSE.txt for details.
//======================================================================================
package gov.nih.nci.caarray.util;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.io.StringReader;
import java.io.StringWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Properties;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import com.csvreader.CsvReader;
import com.csvreader.CsvWriter;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.io.ByteStreams;
import com.google.common.io.OutputSupplier;
import com.google.inject.Binder;
import com.google.inject.name.Names;

/**
 * Utility classes for our project.
 */
@SuppressWarnings({ "PMD.CyclomaticComplexity", "PMD.ExcessiveClassLength" })
public final class CaArrayUtils {
    private static final Logger LOG = Logger.getLogger(CaArrayUtils.class);
    private static final SortedSet<Object> EMPTY_SORTED_SET = new TreeSet<Object>();

    private CaArrayUtils() {
        // prevent instantiation;
    }

    /**
     * Method to take a get a unique result from a set and return it or null.
     * 
     * @param <T> the type of the returned object
     * @param results the set of results returned from a query
     * @return the first result in the set or null
     */
    public static <T> T uniqueResult(Collection<T> results) {
        return results.isEmpty() ? null : results.iterator().next();
    }

    /**
     * Returns an empty collection or map of the appropriate type for a given collection class. By default, returns an
     * empty list, but will return an empty set, empty sorted set, or empty map if the passed in type is a subclass of
     * Set, SortedSet, or Map respectively.
     * 
     * @param collectionType the class of whose type to return an empty collection or map
     * @return the empty collection or map
     */
    public static Object emptyCollectionOrMapFor(Class<?> collectionType) {
        Object val = Collections.EMPTY_LIST;
        if (SortedSet.class.isAssignableFrom(collectionType)) {
            val = EMPTY_SORTED_SET;
        } else if (Set.class.isAssignableFrom(collectionType)) {
            val = Collections.EMPTY_SET;
        } else if (List.class.isAssignableFrom(collectionType)) {
            val = Collections.EMPTY_LIST;
        } else if (Map.class.isAssignableFrom(collectionType)) {
            val = Collections.EMPTY_MAP;
        }
        return val;
    }

    /**
     * Removes matched quotes (single or double) from a string. Quotes are only removed from the first and last
     * characters of the string.
     * 
     * @param str string to dequote
     * @return the dequoted string or the original string, if no changes were made
     */
    public static String dequoteString(String str) {
        if (str != null && str.length() > 1 && ((str.charAt(0) == '"' || str.charAt(0) == '\'')
                && str.charAt(str.length() - 1) == str.charAt(0))) {
            return str.substring(1, str.length() - 1);
        }
        return str;
    }

    /**
     * For given class, returns a ReflectionHelper instance with property accessors for the class.
     * 
     * @param clazz the class
     * @return the ReflectionHelper
     */
    public static ReflectionHelper createReflectionHelper(Class<?> clazz) {
        final List<PropertyAccessor> accessors = new ArrayList<PropertyAccessor>();

        Class<?> currentClass = clazz;
        while (currentClass != null) {
            final Method[] methods = currentClass.getDeclaredMethods();
            for (final Method getter : methods) {
                if (getter.getName().startsWith("get") && getter.getParameterTypes().length == 0) {
                    for (final Method setter : methods) {
                        if (setter.getName().equals('s' + getter.getName().substring(1))
                                && setter.getParameterTypes().length == 1
                                && Void.TYPE.equals(setter.getReturnType())
                                && getter.getReturnType().equals(setter.getParameterTypes()[0])) {
                            getter.setAccessible(true);
                            setter.setAccessible(true);
                            accessors.add(new PropertyAccessor(getter, setter));
                        }
                    }
                }
            }

            currentClass = currentClass.getSuperclass();
        }

        return new ReflectionHelper(accessors.toArray(new PropertyAccessor[accessors.size()]));
    }

    /**
     * For each String bean property on o, if o is blank or empty, converts that property to null.
     * 
     * @param o object to convert properties on.
     */
    public static void blankStringPropsToNull(Object o) {
        if (o == null) {
            return;
        }

        final ReflectionHelper helper = createReflectionHelper(o.getClass());
        for (final PropertyAccessor accessor : helper.getAccessors()) {
            if (accessor.getType().equals(String.class)) {
                try {
                    if (StringUtils.isBlank((String) accessor.get(o))) {
                        accessor.set(o, null);
                    }
                } catch (final IllegalArgumentException e) {
                    LOG.debug(e.getMessage(), e);
                } catch (final IllegalAccessException e) {
                    LOG.debug(e.getMessage(), e);
                } catch (final InvocationTargetException e) {
                    LOG.debug(e.getMessage(), e);
                }
            }
        }
    }

    /**
     * <p>
     * Joins the elements of the provided array into a single String containing the provided list of elements.
     * </p>
     * 
     * @param values the values to join together, may be null (in which case an empty String is returned)
     * @param separator the separator to use
     * @return the joined String, empty if null array input
     */
    public static String join(boolean[] values, String separator) {
        if (values == null || values.length == 0) {
            return StringUtils.EMPTY;
        }
        final StringBuilder sb = new StringBuilder();
        sb.append(values[0]);
        for (int i = 1; i < values.length; i++) {
            sb.append(separator).append(values[i]);
        }
        return sb.toString();
    }

    /**
     * <p>
     * Joins the elements of the provided array into a single String containing the provided list of elements.
     * </p>
     * 
     * @param values the values to join together, may be null (in which case an empty String is returned)
     * @param separator the separator to use
     * @return the joined String, empty if null array input
     */
    public static String join(int[] values, String separator) {
        if (values == null || values.length == 0) {
            return StringUtils.EMPTY;
        }
        final StringBuilder sb = new StringBuilder();
        sb.append(values[0]);
        for (int i = 1; i < values.length; i++) {
            sb.append(separator).append(values[i]);
        }
        return sb.toString();
    }

    /**
     * <p>
     * Joins the elements of the provided array into a single String containing the provided list of elements.
     * </p>
     * 
     * @param values the values to join together, may be null (in which case an empty String is returned)
     * @param separator the separator to use
     * @return the joined String, empty if null array input
     */
    public static String join(long[] values, String separator) {
        if (values == null || values.length == 0) {
            return StringUtils.EMPTY;
        }
        final StringBuilder sb = new StringBuilder();
        sb.append(values[0]);
        for (int i = 1; i < values.length; i++) {
            sb.append(separator).append(values[i]);
        }
        return sb.toString();
    }

    /**
     * <p>
     * Joins the elements of the provided array into a single String containing the provided list of elements.
     * </p>
     * 
     * @param values the values to join together, may be null (in which case an empty String is returned)
     * @param separator the separator to use
     * @return the joined String, empty if null array input
     */
    public static String join(short[] values, String separator) {
        if (values == null || values.length == 0) {
            return StringUtils.EMPTY;
        }
        final StringBuilder sb = new StringBuilder();
        sb.append(values[0]);
        for (int i = 1; i < values.length; i++) {
            sb.append(separator).append(values[i]);
        }
        return sb.toString();
    }

    /**
     * <p>
     * Joins the elements of the provided array into a single String containing the provided list of elements.
     * </p>
     * 
     * @param values the values to join together, may be null (in which case an empty String is returned)
     * @param separator the separator to use
     * @return the joined String, empty if null array input
     */
    public static String join(double[] values, String separator) {
        if (values == null || values.length == 0) {
            return StringUtils.EMPTY;
        }
        final StringBuilder sb = new StringBuilder();
        sb.append(values[0]);
        for (int i = 1; i < values.length; i++) {
            sb.append(separator).append(toXmlString(values[i]));
        }
        return sb.toString();
    }

    /**
     * <p>
     * Joins the elements of the provided array into a single String containing the provided list of elements.
     * </p>
     * 
     * @param values the values to join together, may be null (in which case an empty String is returned)
     * @param separator the separator to use
     * @return the joined String, empty if null array input
     */
    public static String join(float[] values, String separator) {
        if (values == null || values.length == 0) {
            return StringUtils.EMPTY;
        }
        final StringBuilder sb = new StringBuilder();
        sb.append(values[0]);
        for (int i = 1; i < values.length; i++) {
            sb.append(separator).append(toXmlString(values[i]));
        }
        return sb.toString();
    }

    /**
     * Joins the given values as a comma-separated string. Each value will be encoded in this string by escaping any
     * commas in the value with a backslash.
     * 
     * @param values the values to join (null is acceptable).
     * @return the CSV string consisting of the values. If the values were null, an empty String.
     */
    public static String joinAsCsv(String[] values) {
        if (values == null) {
            return StringUtils.EMPTY;
        }
        try {
            final StringWriter sw = new StringWriter();
            final CsvWriter csvWriter = new CsvWriter(sw, ',');
            csvWriter.setEscapeMode(CsvWriter.ESCAPE_MODE_BACKSLASH);
            csvWriter.setUseTextQualifier(false);
            csvWriter.writeRecord(values);
            csvWriter.flush();
            csvWriter.close();
            return sw.toString();
        } catch (final IOException e) {
            throw new IllegalStateException("Could not encode as CSV record: " + e, e);
        }
    }

    /**
     * <p>
     * Splits the provided text into an array of parsed boolean values, using specified separator.
     * </p>
     * 
     * <p>
     * Each value should be encoded using the literal representation of the XML Schema xs:boolean type
     * </p>
     * 
     * @param s the string to parse
     * @param separator the separator between values
     * @return the array of parsed values
     */
    public static boolean[] splitIntoBooleans(String s, String separator) {
        final String[] splits = StringUtils.split(s, separator);
        final boolean[] values = new boolean[splits.length];
        for (int i = 0; i < splits.length; i++) {
            values[i] = xmlStringToBoolean(splits[i]);
        }
        return values;
    }

    /**
     * <p>
     * Splits the provided text into an array of parsed short values, using specified separator.
     * </p>
     * 
     * <p>
     * Each value should be encoded using the literal representation of the XML Schema xs:short type
     * </p>
     * 
     * @param s the string to parse
     * @param separator the separator between values
     * @return the array of parsed values
     */
    public static short[] splitIntoShorts(String s, String separator) {
        final String[] splits = StringUtils.split(s, separator);
        final short[] values = new short[splits.length];
        for (int i = 0; i < splits.length; i++) {
            values[i] = Short.parseShort(splits[i]);
        }
        return values;
    }

    /**
     * <p>
     * Splits the provided text into an array of parsed long values, using specified separator.
     * </p>
     * 
     * <p>
     * Each value should be encoded using the literal representation of the XML Schema xs:long type
     * </p>
     * 
     * @param s the string to parse
     * @param separator the separator between values
     * @return the array of parsed values
     */
    public static long[] splitIntoLongs(String s, String separator) {
        final String[] splits = StringUtils.split(s, separator);
        final long[] values = new long[splits.length];
        for (int i = 0; i < splits.length; i++) {
            values[i] = Long.parseLong(splits[i]);
        }
        return values;
    }

    /**
     * <p>
     * Splits the provided text into an array of parsed int values, using specified separator.
     * </p>
     * 
     * <p>
     * Each value should be encoded using the literal representation of the XML Schema xs:int type
     * </p>
     * 
     * @param s the string to parse
     * @param separator the separator between values
     * @return the array of parsed values
     */
    public static int[] splitIntoInts(String s, String separator) {
        final String[] splits = StringUtils.split(s, separator);
        final int[] values = new int[splits.length];
        for (int i = 0; i < splits.length; i++) {
            values[i] = Integer.parseInt(splits[i]);
        }
        return values;
    }

    /**
     * <p>
     * Splits the provided text into an array of parsed float values, using specified separator.
     * </p>
     * 
     * <p>
     * Each value should be encoded using the literal representation of the XML Schema xs:float type
     * </p>
     * 
     * @param s the string to parse
     * @param separator the separator between values
     * @return the array of parsed values
     */
    public static float[] splitIntoFloats(String s, String separator) {
        final String[] splits = StringUtils.split(s, separator);
        final float[] values = new float[splits.length];
        for (int i = 0; i < splits.length; i++) {
            values[i] = xmlStringToFloat(splits[i]);
        }
        return values;
    }

    /**
     * <p>
     * Splits the provided text into an array of parsed double values, using specified separator.
     * </p>
     * 
     * <p>
     * Each value should be encoded using the literal representation of the XML Schema xs:double type
     * </p>
     * 
     * @param s the string to parse
     * @param separator the separator between values
     * @return the array of parsed values
     */
    public static double[] splitIntoDoubles(String s, String separator) {
        final String[] splits = StringUtils.split(s, separator);
        final double[] values = new double[splits.length];
        for (int i = 0; i < splits.length; i++) {
            values[i] = xmlStringToDouble(splits[i]);
        }
        return values;
    }

    /**
     * <p>
     * Splits the provided CSV String into an array of parsed values.
     * </p>
     * 
     * Each value within the String will be unescaped by converting any backslash-comma combinations back to commas.
     * 
     * @param s string containing a comma-separated list of strings.
     * @return the array of parsed Strings. If s did not contain any comma separated Strings, an empty String. If s was
     *         not a valid CSV string, an IllegalArgumentException is thrown.
     */
    public static String[] splitFromCsv(String s) {
        try {
            final CsvReader csvReader = new CsvReader(new StringReader(s), ',');
            csvReader.setEscapeMode(CsvReader.ESCAPE_MODE_BACKSLASH);
            csvReader.setUseTextQualifier(false);
            String[] values = ArrayUtils.EMPTY_STRING_ARRAY;
            if (csvReader.readRecord()) {
                final int length = csvReader.getColumnCount();
                values = new String[length];
                for (int i = 0; i < length; i++) {
                    values[i] = csvReader.get(i);
                }
            }
            csvReader.close();
            return values;
        } catch (final IOException e) {
            throw new IllegalArgumentException("Could not parse as CSV record: " + s, e);
        }
    }

    private static String toXmlString(float value) {
        if (Float.isNaN(value)) {
            return "NaN";
        } else if (value == Float.POSITIVE_INFINITY) {
            return "INF";
        } else if (value == Float.NEGATIVE_INFINITY) {
            return "-INF";
        } else {
            return Float.toString(value);
        }
    }

    private static String toXmlString(double value) {
        if (Double.isNaN(value)) {
            return "NaN";
        } else if (value == Double.POSITIVE_INFINITY) {
            return "INF";
        } else if (value == Double.NEGATIVE_INFINITY) {
            return "-INF";
        } else {
            return Double.toString(value);
        }
    }

    private static boolean xmlStringToBoolean(String value) {
        if ("true".equals(value) || "1".equals(value)) {
            return true;
        } else if ("false".equals(value) || "0".equals(value)) {
            return false;
        } else {
            throw new IllegalArgumentException(value + " is not a valid boolean");
        }
    }

    private static float xmlStringToFloat(String value) {
        if ("NaN".equals(value)) {
            return Float.NaN;
        } else if ("INF".equals(value)) {
            return Float.POSITIVE_INFINITY;
        } else if ("-INF".equals(value)) {
            return Float.NEGATIVE_INFINITY;
        } else {
            return Float.parseFloat(value);
        }
    }

    private static double xmlStringToDouble(String value) {
        if ("NaN".equals(value)) {
            return Double.NaN;
        } else if ("INF".equals(value)) {
            return Double.POSITIVE_INFINITY;
        } else if ("-INF".equals(value)) {
            return Double.NEGATIVE_INFINITY;
        } else {
            return Double.parseDouble(value);
        }
    }

    /**
     * Return the constant names of the given enum instances.
     * 
     * @param <E> the type of the enum instances
     * @param enums the enum instances whose constant names to return
     * @return the names, e.g. the set of enum.name() for each enum in enums
     */
    public static <E extends Enum<E>> Set<String> namesForEnums(Iterable<E> enums) {
        final Set<String> names = new HashSet<String>();
        for (final Enum<?> oneEnum : enums) {
            names.add(oneEnum.name());
        }
        return names;
    }

    /**
     * Serializes the given object (zipped) to a byte array.
     * 
     * @param serializable object to serialize
     * @return the serialized object as a byte array.
     */
    public static byte[] serialize(Serializable serializable) {
        final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        GZIPOutputStream gZipOutputStream = null;
        ObjectOutputStream objectOutputStream = null;
        try {
            gZipOutputStream = new GZIPOutputStream(byteArrayOutputStream);
            objectOutputStream = new ObjectOutputStream(gZipOutputStream);
            objectOutputStream.writeObject(serializable);
            objectOutputStream.flush();
            gZipOutputStream.finish();
            gZipOutputStream.flush();
            byteArrayOutputStream.flush();
        } catch (final IOException e) {
            throw new IllegalStateException("Couldn't serialize object", e);
        } finally {
            IOUtils.closeQuietly(objectOutputStream);
            IOUtils.closeQuietly(gZipOutputStream);
            IOUtils.closeQuietly(byteArrayOutputStream);
        }
        return byteArrayOutputStream.toByteArray();
    }

    /**
     * Deserializes an object from a gzipped serialized representation.
     * 
     * @param bytes the byte array containing the gzipped serialized representation of an object
     * @return the deserialized object
     */
    public static Serializable deserialize(byte[] bytes) {
        final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
        final Serializable value = deserialize(byteArrayInputStream);
        IOUtils.closeQuietly(byteArrayInputStream);
        return value;
    }

    /**
     * Deserializes an object from a gzipped serialized representation.
     * 
     * @param is the InputStream which will produce the gzipped serialized representation of an object
     * @return the deserialized object
     */
    public static Serializable deserialize(InputStream is) {
        GZIPInputStream gzipInputStream = null;
        ObjectInputStream objectInputStream = null;
        Serializable object = null;
        try {
            gzipInputStream = new GZIPInputStream(is);
            objectInputStream = new ObjectInputStream(gzipInputStream);
            object = (Serializable) objectInputStream.readObject();
        } catch (final IOException e) {
            throw new IllegalStateException("Couldn't read object", e);
        } catch (final ClassNotFoundException e) {
            throw new IllegalStateException("Couldn't read object", e);
        } finally {
            IOUtils.closeQuietly(objectInputStream);
            IOUtils.closeQuietly(gzipInputStream);
        }
        return object;
    }

    /**
     * Returns the first element in iterable that satisfies the given predicate, or null if no elements do. This is
     * identical to Iterables.find from Google's Collections library, except it returns null rather than throw
     * NoSuchElementException if there are no matches.
     * 
     * @param <T> the type of elements of iterable
     * @param iterable the iterable to search throw
     * @param predicate the predicate to satisfy
     * @return the first element matching the predicate, or null if no matches
     */
    public static <T> T find(Iterable<T> iterable, Predicate<? super T> predicate) {
        try {
            return Iterables.find(iterable, predicate);
        } catch (final NoSuchElementException e) {
            return null;
        }
    }

    /**
     * Utility method to create a URI from given string without throwing a checked exception. This simply wraps new
     * URI(uri), throwing a runtime exception if this results in a URISyntaxException. For use with known-good uri
     * strings, to avoid having a try-catch block.
     * 
     * @param uri the URI string
     * @return the URI corresponding to uri
     */
    public static URI makeUriQuietly(String uri) {
        try {
            return new URI(uri);
        } catch (final URISyntaxException e) {
            throw new IllegalStateException("Couldn't create dummy URI");
        }
    }

    /**
     * Utility method to create a URI from given scheme and scheme specific part without throwing a checked exception.
     * This simply wraps new URI(scheme, schemeSpecificPart), throwing a runtime exception if this results in a
     * URISyntaxException. For use with known-good scheme and scheme specific part, to avoid having a try-catch block.
     * 
     * @param scheme the scheme
     * @param schemeSpecificPart the schemeSpecificPart
     * @return the URI corresponding to scheme:schemeSpecificPart
     */
    public static URI makeUriQuietly(String scheme, String schemeSpecificPart) {
        try {
            return new URI(scheme, schemeSpecificPart, null);
        } catch (final URISyntaxException e) {
            throw new IllegalStateException("Couldn't create dummy URI");
        }
    }

    /**
     * returns a byte array representing the input byte array, gzipped. Use this method with caution - for large arrays,
     * you should use streams instead.
     * 
     * @param input the content to compress
     * @return the gzipped input
     * @throws IOException if there is a problem zipping the content
     */
    public static byte[] gzip(byte[] input) throws IOException {
        final ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ByteStreams.write(input, new OutputSupplier<OutputStream>() {
            @Override
            public OutputStream getOutput() throws IOException {
                return new GZIPOutputStream(baos);
            }
        });
        baos.close();
        return baos.toByteArray();
    }

    /**
     * returns a byte array representing the input byte array, un-gzipped. Use this method with caution - for large
     * arrays, you should use streams instead.
     * 
     * @param input the content to uncompress, should be in gzip format
     * @return the uncompressed input
     * @throws IOException if there is a problem uncompressing the content, including if the input is not gzipped
     */
    public static byte[] gunzip(byte[] input) throws IOException {
        final InputStream in = new GZIPInputStream(new ByteArrayInputStream(input));
        final byte[] output = ByteStreams.toByteArray(in);
        in.close();
        return output;
    }

    /**
     * Load a properties file from given classpath resource, and create a constant binding to @Named(key) for each
     * property using the given binder. If the resource cannot be loaded, does nothing.
     * 
     * @param resourceName the resource to load. it is loaded using the classloader of this class.
     * @param binder the binder to use for pinding the key=property values
     */
    public static void bindPropertiesAsNamed(String resourceName, Binder binder) {
        final Properties props = new Properties();
        try {
            props.load(CaArrayUtils.class.getResourceAsStream(resourceName));
        } catch (final IOException e) {
            LOG.warn("Could not load properties from " + resourceName);
        }
        Names.bindProperties(binder, props);
    }
}