org.regenstrief.util.Util.java Source code

Java tutorial

Introduction

Here is the source code for org.regenstrief.util.Util.java

Source

/**
 * The contents of this file are subject to the Regenstrief Public License
 * Version 1.0 (the "License"); you may not use this file except in compliance with the License.
 * Please contact Regenstrief Institute if you would like to obtain a copy of the license.
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations
 * under the License.
 *
 * Copyright (C) Regenstrief Institute.  All Rights Reserved.
 */
package org.regenstrief.util;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.regenstrief.io.BomReader;
import org.regenstrief.io.IoUtil;
import org.regenstrief.io.LabeledInputStream;
import org.regenstrief.io.LabeledReader;
import org.regenstrief.util.criterion.AssignableFromCriterion;
import org.regenstrief.util.criterion.Criteria;
import org.regenstrief.util.criterion.Criterion;
import org.regenstrief.util.property.PropertyResolver;
import org.regenstrief.util.property.RegenPropertyResolver;
import org.regenstrief.util.reflect.ReflectUtil;

import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Modifier;
import java.net.*;
import java.security.MessageDigest;
import java.security.Security;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.Callable;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.zip.GZIPInputStream;
import java.util.zip.ZipInputStream;

/**
 * Title: Utilities Description: List of arbitrary <code>static</code> methods not logically within
 * a specific object. Copyright: Copyright (c) 2001 Company: Regenstrief Institute
 * 
 * @author Lonnie Blevins
 * @version 1.0
 */
public final class Util {

    /** Nobody should create a <code>Util</code> object. It's just here for its static methods. **/
    private Util() {
    }

    /**
     * generate a unique id number, for lifetime of the applciation anyway
     * 
     * @return unique long integer (for lifetime of the application)
     **/
    public final synchronized static long generateUniqueID() {
        return ++uniqueID;
    }

    /**
     * generate a unique id number, for lifetime of the applciation anyway
     * 
     * @return unique long integer (for lifetime of the application)
     **/
    public final synchronized static String generateUniqueIDString() {
        return Long.toString(generateUniqueID());
    }

    private static PropertyResolver propertyResolver = new RegenPropertyResolver();

    private static long uniqueID = 0; //*Unique ID will start at one every time application begins

    private static Log log = LogFactory.getLog(Util.class);

    private static MyAuthenticator myAuthenticator = null;

    private final static ThreadLocal<String> authenticatorUser = new ThreadLocal<String>();

    private final static ThreadLocal<String> authenticatorPassword = new ThreadLocal<String>();

    private static Field threadTarget = null;

    private static String illegalFileNameCharacters = null;

    //private static boolean classpathValidated = false;
    public final static int BUFFER_SIZE = 4096;

    private final static int MAX_MARK_BUFFER = 10000000;

    public final static String DEFAULT_PROPERTIES = "rhits.properties";

    public final static String PROPERTIES = "org.regenstrief.database.Properties";

    public final static String PROP_APPLICATION = "org.regenstrief.database.APPLICATION";

    public final static String PROP_EMAIL_ADDRESS = "org.regenstrief.database.EMAIL.ADDRESS";

    public final static String PROP_EMAIL_ADDRESS_SENDER = "org.regenstrief.database.EMAIL.ADDRESS.SENDER";

    public final static String PROP_EMAIL_HOST = "org.regenstrief.database.EMAIL.HOST";

    public final static String PROP_EMAIL_PORT = "org.regenstrief.database.EMAIL.PORT";

    public final static String PROP_EMAIL_SERVER = "org.regenstrief.database.EMAIL.SERVER";

    public final static String PROP_PREFIX = Util.class.getName() + ".";

    public final static String PROP_DEBUG = PROP_PREFIX + "debug";

    public final static String PROP_LAZY_LOAD = PROP_PREFIX + "lazyLoad";

    public final static String PROP_INPUT_DELIMITER = PROP_PREFIX + "inputDelimiter";

    public final static String PROP_TRUST_STORE = "javax.net.ssl.trustStore";

    public final static String PROP_TRUST_STORE_PASSWORD = "javax.net.ssl.trustStorePassword";

    public final static String PROP_AUTHENTICATOR_USER = "org.regenstrief.util.AuthenticatorUser";

    public final static String PROP_AUTHENTICATOR_PASSWORD = "org.regenstrief.util.AuthenticatorPassword";

    public final static String PROP_USED_MEMORY_PERCENTAGE_CEILING = "org.regenstrief.util.UsedMemoryPercentageCeiling";

    public final static String PROP_LOG_OUTPUT = "org.regenstrief.util.Util.LOG_OUTPUT";

    public final static String PROP_LOG_ERROR = "org.regenstrief.util.Util.LOG_ERROR";

    public final static String PROP_EXCEPTION_REPORTING = "org.regenstrief.util.Util.EXCEPTION_REPORTING";

    public final static String PROP_HANDLER = "java.protocol.handler.pkgs";

    public final static String PROP_INFO = PROP_PREFIX + PROP_PREFIX + "info";

    public final static String HANDLER_SUN_SSL = "com.sun.net.ssl.internal.www.protocol";

    public final static String PARTIAL_KEY_JDBC_DRIVER = "JDBC.DRIVER_CLASS_NAME";

    public final static String PARTIAL_KEY_JDBC_ADAPTER = "JDBC.ADAPTER_CLASS_NAME";

    public final static String PARTIAL_KEY_JDBC_URL = "JDBC.URL";

    public final static String PARTIAL_KEY_JDBC_USERNAME = "JDBC.USERNAME";

    public final static String PARTIAL_KEY_JDBC_PASSWORD = "JDBC.PASSWORD";

    public final static int EXCEPTION_NONE = 0;

    public final static int EXCEPTION_GET_MESSAGE = 1;

    public final static int EXCEPTION_TO_STRING = 2;

    public final static int EXCEPTION_STACK_TRACE = 3;

    public final static int EXCEPTION_CAUSE_STRINGS = 4;

    public final static String DEFAULT_CONCAT_DELIM = " ";

    public final static String TRUE = Boolean.toString(true);

    public final static String FALSE = Boolean.toString(false);

    public final static String BR = getLineSeparator();

    public final static int[] EMPTY_ARRAY_INT = {};

    public final static String[] EMPTY_ARRAY_STRING = {};

    public final static Class<?>[] EMPTY_ARRAY_CLASS = {};

    public final static Object[] EMPTY_ARRAY_OBJECT = {};

    /**
     * Parses boolean value from text, such as input or control file.
     * 
     * @param val the boolean value
     * @return <code>true</code> is returned if value is "TRUE", "YES", "Y", "YE", "ON", or "1"
     *         (case-insensitive test) <br>
     *         <code>false</code> is returned otherwise.
     **/
    public final static boolean parseBoolean(final String val) {
        if (val == null) {
            return false;
        }
        final String x = val.trim(); //=Normalize all values
        if (x.equals("1") || x.equalsIgnoreCase(TRUE) || x.equalsIgnoreCase("YES") || x.equalsIgnoreCase("Y")
                || x.equalsIgnoreCase("YE") || x.equalsIgnoreCase("ON")) {
            return true;
        }
        return false; //=Default is FALSE
    }

    /**
     * Get the <code>boolean</code> value of the <code>Boolean</code>. This defaults to false if the
     * Boolean's value is <code>null</code>.
     * 
     * @param booleanValue the value of the Boolean
     * @return boolean value for the Boolean handling <code>null</code> case as false
     */
    public final static boolean booleanValue(final Boolean booleanValue) {
        return booleanValue == null ? false : booleanValue.booleanValue();
    }

    /**
     * parses String txt into a Vector of String objects by breaking it at every occurrence of a
     * delimiter character
     * 
     * @param txtValue text to be parsed
     * @param dlmChar delimiter character at which to split the <code>txtValue</code>
     * @return list of text values parsed out of <code>txtValue</code>; <br>
     *         <code>dlmChar</code> will not be included in any of the returned text values; <br>
     *         if <code>dlmChar</code> appears N times in <code>txtValue</code>, N+1 values will be
     *         returned
     **/
    public final static Vector<String> parseString(final String txtValue, final char dlmChar) {
        return parseString(txtValue, (new Character(dlmChar)).toString());
    }

    /**
     * parses String text into a Vector of String objects by breaking it at every occurrence of a
     * delimiter text string
     * 
     * @param txt text to be parsed
     * @param dlm delimiter String at which to split the <code>txtValue</code>
     * @return list of text values parsed out of <code>txtValue</code>; <br>
     *         <code>dlm</code> will not be included in any of the returned text values; <br>
     *         if <code>dlm</code> appears N times in <code>txtValue</code>, N+1 values will be
     *         returned
     **/
    public final static Vector<String> parseString(String txt, final String dlm) {
        if (isEmpty(txt)) {
            return null;
        }
        final Vector<String> lst = new Vector<String>(); //=Make guess at initial allocation
        final int bgnPos = 0; //=Character position parsing values.
        int endPos = -1, bgnPosNext = 0;
        String firstChar; //=First char of value
        int txtLen; //=Curr size of value
        final int dlmLen = dlm.length(); //=Size of delimiter String

        while ((txtLen = txt.length()) != 0) { //=Loop until parse entire value
            if (txt.startsWith("'") || txt.startsWith("\"")) {
                //=IF value begins with a double or single
                //quotation mark,
                firstChar = txt.substring(0, 1); //THEN find out which quotation mark it is.
                endPos = txt.indexOf(firstChar); //=Look for closing quotation mark followed by delimiter
                bgnPosNext = endPos + dlmLen; //=Calculate where following value will start.
                if (endPos != -1) { //=IF found matching quotation mark
                    if (endPos < txtLen) { //THEN IF is not at end of string
                        if (!txt.substring(endPos + 1, bgnPosNext).equals(dlm)) {
                            endPos = -1;
                        }
                        //     THEN IF delimiter does not follow quotation mark,
                        //          THEN revert to pure delimiter search.
                    }
                }
            } else {
                endPos = -1;
            }
            if (endPos == -1) { //=IF parsing by quotation mark did not work out
                endPos = txt.indexOf(dlm); //THEN revert to looking for delimiter by itself.
                if (endPos == -1) { //=IF no more delimiters
                    endPos = txtLen;
                    bgnPosNext = endPos;
                } else {
                    bgnPosNext = endPos + dlmLen; //=Calculate start of following value.
                }
            }
            lst.addElement(txt.substring(bgnPos, endPos)); //=Add single text element to value
            if (bgnPosNext >= txtLen) { //=IF exhausted all values
                break; //THEN exit loop
            } else {
                txt = txt.substring(bgnPosNext); //ELSE move parsing past the delimiter
            }
        }
        return lst; //=Return Vector to CALLer
    }

    /**
     * Parses an int from a substring of the given String; should yield the same results as
     * Integer.parseInt(s.substring(startIndex, endIndex), but should be more efficient
     * 
     * @param s the String
     * @param startIndex the start index of the substring
     * @param endIndex the end index of the substring
     * @return the int
     **/
    public final static int parseInt(final String s, final int startIndex, final int endIndex) {
        int n = 0, sign = 1;
        if (startIndex >= endIndex) {
            throw new StringIndexOutOfBoundsException(endIndex - startIndex);
        }
        for (int i = startIndex; i < endIndex; i++) {
            final char c = s.charAt(i);
            if (i == startIndex) {
                if (c == '-') {
                    sign = -1;
                    continue;
                } else if (c == '+') {
                    // Java 7 introduced support for leading + character
                    continue;
                }
            }
            if ((c < '0') || (c > '9')) {
                throw new NumberFormatException(s);
            }
            n = (n * 10) + (c - '0');
        }

        return sign * n;
    }

    public final static int parseInt(final String s, final int startIndex) {
        return parseInt(s, startIndex, s.length());
    }

    /*
    public final static <E extends Enum<E>> E parse(final String text, final E... vals) {
    for (final E val : vals) {
        if (val.name().equalsIgnoreCase(text)) {
            return val;
        }
    }
    return null;
    }
    */

    public final static <E> E parse(final String text, final E... vals) {
        for (final E val : vals) {
            if (val.toString().equalsIgnoreCase(text)) {
                return val;
            }
        }
        return null;
    }

    /**
     * Determines if a String and a char are equal
     * 
     * @param s the String
     * @param c the char
     * @return whether or not s equals c
     **/
    public final static boolean equals(final String s, final char c) {
        return (length(s) == 1) && (s.charAt(0) == c);
    }

    /**
     * Determines if two Strings are equal, ignoring case (true if both are null)
     * 
     * @param s1 String 1
     * @param s2 String 2
     * @return whether s1 equals s2, ignoring case
     **/
    public final static boolean equalsIgnoreCase(final String s1, final String s2) {
        return s1 == null ? s2 == null : s1.equalsIgnoreCase(s2);
    }

    /**
     * Retrieves a comparison of two Strings
     * 
     * @param c1 the first String
     * @param c2 the second String
     * @return -1 if c1 comes before c2, +1 if c2 comes first, otherwise 0
     **/
    public final static int compareToIgnoreCase(final String c1, final String c2) {
        if (c1 == null) {
            return c2 == null ? 0 : 1;
        }
        return c2 == null ? -1 : c1.compareToIgnoreCase(c2);
    }

    /**
     * Converts a string to upper case letters
     * 
     * @param s the String to convert
     * @return the upper case String
     **/
    public final static String toUpperCase(final String s) {
        return s == null ? null : s.toUpperCase();
    }

    /**
     * Converts a string to lower case letters
     * 
     * @param s the String to convert
     * @return the lower case String
     **/
    public final static String toLowerCase(final String s) {
        return s == null ? null : s.toLowerCase();
    }

    public final static String toProperName(final String s) {
        return isEmpty(s) ? s : Character.toUpperCase(s.charAt(0)) + s.substring(1).toLowerCase();
    }

    /**
     * Retrieves the String or an empty String if it is null
     * 
     * @param s the String
     * @return the String or an empty String if it is null
     **/
    public final static String unNull(final String s) {
        return nvl(s, "");
    }

    /**
     * Like Oracle's nvl, which is like coalesce with only two arguments
     * 
     * @param t the desired field
     * @param def the default field
     * @return t if not null, otherwise def
     */
    public final static <T> T nvl(final T t, final T def) {
        return t == null ? def : t;
    }

    /**
     * Like nvl, but treats empty Strings like null
     * 
     * @param s the desired String
     * @param def the default String
     * @return s if not empty, otherwise def
     */
    public final static String nvls(final String s, final String def) {
        return isEmpty(s) ? def : s;
    }

    /**
     * Retrieves the first non-null String in the given array
     * 
     * @param a the array
     * @return the first non-null String
     **/
    public final static String coalesce(final String... a) {
        for (final String s : unNull(a)) {
            if (s != null) {
                return s;
            }
        }
        return null;
    }

    /**
     * Converts an object to a string
     * 
     * @param o the Object to convert
     * @return the String
     **/
    public final static String toString(final Object o) {
        return o == null ? null : o.toString();
    }

    /**
     * Retrieves the same String for the given Object as would be returned by the default method
     * toString(), whether or not the given Object's class overrides toString(), the toString
     * equivalent of System.identityHashCode
     * 
     * @param o the Object
     * @return the String
     **/
    public final static String identityToString(final Object o) {
        return o == null ? null : o.getClass().getName() + '@' + Integer.toHexString(System.identityHashCode(o));
    }

    /**
     * Retrieves the hash code of the given Object
     * 
     * @param o the Object
     * @return the hash code
     **/
    public final static int hashCode(final Object o) {
        return o == null ? 0 : o.hashCode();
    }

    /**
     * Converts an array into a String
     * 
     * @param a the array
     * @return the String
     **/
    public final static String arrayToString(final Object[] a) {
        return length(a) == 0 ? null : listToString(Arrays.asList(a));
    }

    /**
     * Converts a Collection into a String
     * 
     * @param c the Collection
     * @return the String
     **/
    public final static String collectionToString(final Collection<?> c) {
        final Iterator<?> iter = c == null ? null : c.iterator();

        if (!hasNext(iter)) {
            return null;
        }

        final StringBuilder sb = new StringBuilder();
        for (; iter.hasNext();) {
            final String s = toString(iter.next());
            if (s != null) {
                sb.append(s);
            }
            if (iter.hasNext()) {
                sb.append('\n');
            }
        }

        return sb.toString();
    }

    /**
     * Converts a Map into a String
     * 
     * @param m the Map
     * @return the String
     **/
    public final static String mapToString(final Map<?, ?> m) {
        return m == null ? null : collectionToString(m.entrySet());
    }

    /**
     * Converts a List into a String
     * 
     * @param l the List
     * @return the String
     **/
    public final static String listToString(final List<?> l) {
        if (!(l instanceof RandomAccess)) {
            return collectionToString(l);
        }

        final int size = size(l);
        if (size == 0) {
            return null;
        }

        final StringBuilder sb = new StringBuilder();
        for (int i = 0; i < size; i++) {
            if (i > 0) {
                sb.append('\n');
            }
            final String s = toString(l.get(i));
            if (s != null) {
                sb.append(s);
            }
        }

        return sb.toString();
    }

    /**
     * Retrieves the Iterable or an empty Iterable if it is null; designed to iterate through an
     * Iterable without an Exception if it's null: Iterable<E> iterable = null; for (E e :
     * Util.unNull(iterable)) { }
     * 
     * @param iterable the Iterable
     * @param <E> the element type
     * @return the Iterable or an empty Iterable if it is null
     **/
    public final static <E> Iterable<E> unNull(Iterable<E> iterable) {
        if (iterable == null) {
            iterable = Collections.emptyList();
        }

        return iterable;
    }

    public final static <E> List<E> unNullList(List<E> list) {
        if (list == null) {
            list = Collections.emptyList();
        }

        return list;
    }

    /**
     * Retrieves the int[] array or an empty int[] array if it is null; designed to iterate through
     * an int[] array without an Exception if it's null: int[] iterable = null; for (int i :
     * Util.unNull(iterable)) { }
     * 
     * @param a the int[] array
     * @return the int[] array or an empty int[] array if it is null
     **/
    public final static int[] unNull(final int[] a) {
        return nvl(a, EMPTY_ARRAY_INT);
    }

    /**
     * Retrieves the String[] array or an empty String[] array if it is null; designed to iterate
     * through a String[] array without an Exception if it's null: String[] iterable = null; for
     * (String i : Util.unNull(iterable)) { }
     * 
     * @param a the int[] array
     * @return the int[] array or an empty int[] array if it is null
     **/
    public final static String[] unNull(final String[] a) {
        return nvl(a, EMPTY_ARRAY_STRING);
    }

    /**
     * Makes a copy, starting over for ConcurrentModificationExceptions
     * 
     * @param iterable the Iterable
     * @param <E> the element type
     * @return the copy
     **/
    public final static <E> Iterable<E> threadSafe(final Iterable<E> iterable) {
        if (iterable == null) {
            return Collections.emptyList();
        }

        final List<E> copy = new ArrayList<E>();
        while (true) {
            try {
                addAll(copy, iterable);
                return copy;
            } catch (final ConcurrentModificationException e) {
                copy.clear(); // Try again
            }
        }
    }

    /**
     * Retrieves the first element of an Iterable
     * 
     * @param iterable the Iterable
     * @param <E> the element type
     * @return the first element
     **/
    public final static <E> E getFirst(final Iterable<E> iterable) {
        final Iterator<E> iter = iterator(iterable);
        return hasNext(iter) ? iter.next() : null;
    }

    public final static <E> Iterator<E> iterator(final Iterable<E> iterable) {
        return iterable == null ? null : iterable.iterator();
    }

    /**
     * Determines if two objects are equal (true if both are null)
     * 
     * @param o1 Object 1
     * @param o2 Object 2
     * @return whether or not o1 equals o2
     **/
    public final static boolean equals(final Object o1, final Object o2) {
        return o1 == null ? o2 == null : o2 == null ? false : o1.equals(o2) || o2.equals(o1);
        // .equals is not always symmetric;
        // for example, if a java.sql.Timestamp t contains the same date as a java.util.Date d,
        // t.equals(d) will be true but d.equals(t) will be false
    }

    /**
     * Determines if two arrays contain the same elements in the same order
     * 
     * @param a1 the first Object[] array
     * @param a2 the second Object[] array
     * @return whether or not a1 contains the same elements as a2
     **/
    public final static boolean equals(final Object[] a1, final Object[] a2) {
        if (a1 == a2) {
            return true;
        }

        if ((a1 == null) || (a2 == null)) { // If both are null, wouldn't get past a1 == a2
            return false;
        } else if (a1.length != a2.length) {
            return false;
        }

        for (int i = 0; i < a1.length; i++) {
            if (!equals(a1[i], a2[i])) {
                return false;
            }
        }

        return true;
    }

    /**
     * Determines if two Iterables contain the same elements in the same order
     * 
     * @param i1 the first Iterable
     * @param i2 the second Iterable
     * @return whether or not i1 contains the same elements as i2
     **/
    public final static boolean equals(final Iterable<?> i1, final Iterable<?> i2) {
        return equals(i1.iterator(), i2.iterator());
    }

    /**
     * Determines if two Iterators contain the same elements in the same order
     * 
     * @param iter1 the first Iterator
     * @param iter2 the second Iterator
     * @return whether or not iter1 contains the same elements as iter2
     **/
    public final static boolean equals(final Iterator<?> iter1, final Iterator<?> iter2) {
        while (iter1.hasNext()) {
            if (!iter2.hasNext()) {
                return false;
            } else if (!equals(iter1.next(), iter2.next())) {
                return false;
            }
        }

        return !iter2.hasNext();
    }

    /**
     * Retrieves a comparison of two Objects
     * 
     * @param c1 the first Comparable Object
     * @param c2 the second Comparable Object
     * @param <T> the type
     * @return -1 if c1 comes before c2, +1 if c2 comes first, otherwise 0
     **/
    public final static <T extends Comparable<? super T>> int compareTo(final T c1, final T c2) {
        return compareTo(c1, c2, true);
    }

    /**
     * Retrieves a comparison of two Objects
     * 
     * @param c1 the first Comparable Object
     * @param c2 the second Comparable Object
     * @param nullsLast whether nulls should be ordered last
     * @param <T> the type
     * @return -1 if c1 comes before c2, +1 if c2 comes first, otherwise 0
     **/
    public final static <T extends Comparable<? super T>> int compareTo(final T c1, final T c2,
            final boolean nullsLast) {
        final int nullCmp = nullsLast ? 1 : -1; // nullsLast name matches DatabaseAccess.orderCriteria
        if (c1 == null) {
            //return c2 == null ? 0 : -c2.compareTo(c1);
            return c2 == null ? 0 : nullCmp;
        }
        //return c1.compareTo(c2); // For some classes, passing a null argument throws a NullPointerException
        return c2 == null ? -nullCmp : c1.compareTo(c2);
    }

    /**
     * Retrieves a comparison of two ints
     * 
     * @param i1 the first int
     * @param i2 the second int
     * @return -1 if i1 comes before i2, +1 if i2 comes first, otherwise 0
     **/
    public final static int compareTo(final int i1, final int i2) {
        if (i1 < i2) {
            return -1;
        }
        return i1 > i2 ? 1 : 0;
    }

    /**
     * Retrieves a comparison of two longs
     * 
     * @param i1 the first long
     * @param i2 the second long
     * @return -1 if i1 comes before i2, +1 if i2 comes first, otherwise 0
     **/
    public final static int compareTo(final long i1, final long i2) {
        if (i1 < i2) {
            return -1;
        }
        return i1 > i2 ? 1 : 0;
    }

    /**
     * Returns the length of the given array
     * 
     * @param array the array for which to return the length
     * @return the length
     **/
    public final static int length(final Object array) {
        if (array == null) {
            return 0;
        } else if (array instanceof byte[]) {
            return ((byte[]) array).length;
        } else if (array instanceof short[]) {
            return ((short[]) array).length;
        } else if (array instanceof int[]) {
            return ((int[]) array).length;
        } else if (array instanceof long[]) {
            return ((long[]) array).length;
        } else if (array instanceof float[]) {
            return ((float[]) array).length;
        } else if (array instanceof double[]) {
            return ((double[]) array).length;
        } else if (array instanceof boolean[]) {
            return ((boolean[]) array).length;
        } else if (array instanceof char[]) {
            return ((char[]) array).length;
        }
        return ((Object[]) array).length;
    }

    /**
     * Returns the length of the given array
     * 
     * @param array the array for which to return the length
     * @return the length
     **/
    public final static int length(final Object[] array) {
        return array == null ? 0 : array.length;
    }

    /**
     * Returns the length of the given array
     * 
     * @param array the array for which to return the length
     * @return the length
     **/
    public final static int length(final byte[] array) {
        return array == null ? 0 : array.length;
    }

    /**
     * Returns the length of the given array
     * 
     * @param array the array for which to return the length
     * @return the length
     **/
    public final static int length(final int[] array) {
        return array == null ? 0 : array.length;
    }

    /**
     * Reverses a byte[] array
     * 
     * @param b the byte[] array
     * @param off the offset at which to
     * @param len int
     **/
    public final static void reverse(final byte[] b, final int off, final int len) {
        final int s = len / 2;
        for (int i = off, j = len - 1; i < s; i++, j--) {
            final byte t = b[i];
            b[i] = b[j];
            b[j] = t;
        }
    }

    /**
     * Sorts the given List
     * 
     * @param l the List to sort
     * @param <E> the element type
     * @param <L> the list type
     * @return the List after sorting it
     **/
    public final static <E extends Comparable<? super E>, L extends List<E>> L sort(final L l) {
        if (l == null) {
            return null;
        }
        Collections.sort(l);

        return l;
    }

    /**
     * Retrieves a List of elements beginIndex through the end of the given List
     * 
     * @param l the List
     * @param beginIndex the begin index
     * @param <E> the element type
     * @return the sub-List
     **/
    public final static <E> List<E> sublist(final List<E> l, final int beginIndex) {
        return sublist(l, beginIndex, l.size());
    }

    /**
     * Retrieves a List of elements beginIndex through endIndex - 1 of the given List
     * 
     * @param l the List
     * @param beginIndex the begin index
     * @param endIndex the end index
     * @param <E> the element type
     * @return the sub-List
     **/
    public final static <E> List<E> sublist(final List<E> l, final int beginIndex, final int endIndex) {
        final List<E> sub = new ArrayList<E>(endIndex - beginIndex);

        for (int i = beginIndex; i < endIndex; i++) {
            sub.add(l.get(i));
        }

        return sub;
    }

    public final static Object[] toArray(final Collection<?> c) {
        return c == null ? null : c.toArray();
    }

    /**
     * Returns the size of the given Collection (List, Set, etc.)
     * 
     * @param c the Collection for which to return the size
     * @return the size
     **/
    public final static int size(final Collection<?> c) {
        return c == null ? 0 : c.size();
    }

    /**
     * Retrieves whether the given Collection has any content
     * 
     * @param c the Collection
     * @return whether the given Collection has any content
     **/
    public final static boolean isValued(final Collection<?> c) {
        return size(c) > 0;
    }

    public final static boolean isEmpty(final Collection<?> c) {
        return size(c) == 0;
    }

    /**
     * Returns the size of the given Map
     * 
     * @param m the Map for which to return the size
     * @return the size
     **/
    public final static int size(final Map<?, ?> m) {
        return m == null ? 0 : m.size();
    }

    /**
     * Retrieves whether the given Map has any content
     * 
     * @param m the Map
     * @return whether the given Map has any content
     **/
    public final static boolean isValued(final Map<?, ?> m) {
        return size(m) > 0;
    }

    /**
     * Returns the size of the given Iterable
     * 
     * @param i the Iterable for which to return the size
     * @return the size
     **/
    public final static int size(final Iterable<?> i) {
        return i == null ? 0 : size(i.iterator());
    }

    /**
     * Retrieves whether the given Iterable has any content
     * 
     * @param i the Iterable
     * @return whether the given Iterable has any content
     **/
    public final static boolean isValued(final Iterable<?> i) {
        return i == null ? false : isValued(i.iterator());
    }

    /**
     * Returns the size of the given Iterator
     * 
     * @param i the Iterator for which to return the size
     * @return the size
     **/
    public final static int size(Iterator<?> i) {
        int size = 0;

        for (; hasNext(i); size++, i.next()) {
            ;
        }
        i = i instanceof ListIterator ? reset((ListIterator<?>) i) : i;

        return size;
    }

    /**
     * Retrieves whether the given Iterator has any content
     * 
     * @param i the Iterator
     * @return whether the given Iterator has any content
     **/
    public final static boolean isValued(final Iterator<?> i) {
        return hasNext(i);
    }

    public final static boolean hasNext(final Iterator<?> i) {
        return (i != null) && i.hasNext();
    }

    /**
     * Converts the given Iterable to a List if needed
     * 
     * @param i the Iterable
     * @param <E> the element type
     * @return the List
     **/
    public final static <E> List<E> toList(final Iterable<E> i) {
        return i instanceof List ? (List<E>) i : list(i);
    }

    /**
     * Retrieves an ArrayList containing the elements of the given Iterable
     * 
     * @param i the Iterable
     * @param <E> the element type
     * @return the ArrayList
     **/
    public final static <E> ArrayList<E> list(final Iterable<E> i) {
        return list(i.iterator());
    }

    /**
     * Retrieves an ArrayList containing the elements of the given Iterator
     * 
     * @param i the Iterator
     * @param <E> the element type
     * @return the ArrayList
     **/
    public final static <E> ArrayList<E> list(final Iterator<E> i) {
        return list(i, new ArrayList<E>());
    }

    /**
     * Retrieves a Collection containing the elements of the given Iterator
     * 
     * @param i the Iterator
     * @param c the Collection in which to store the Iterator contents
     * @param <C> the Collection type
     * @param <E> the element type
     * @return the List
     **/
    public final static <C extends Collection<E>, E> C list(Iterator<E> i, final C c) {
        for (; hasNext(i); c.add(i.next())) {
            ;
        }
        i = i instanceof ListIterator ? reset((ListIterator<E>) i) : i;

        return c;
    }

    public final static <E> Iterable<E> wrap(final Iterator<E> iter) {
        return new Iterable<E>() {
            @Override
            public Iterator<E> iterator() {
                return iter;
            }
        };
    }

    /**
     * Resets a ListIterator
     * 
     * @param i the ListIterator
     * @param <E> the element type
     * @return the ListIterator
     **/
    public final static <E> ListIterator<E> reset(final ListIterator<E> i) {
        for (; (i != null) && i.hasPrevious(); i.previous()) {
            ;
        }
        return i;
    }

    /**
     * Adds all elements of the source Iterable to the destination Collection
     * 
     * @param dst the destination Collection
     * @param src the source Iterable
     * @param <E> the element type
     **/
    public final static <E> void addAll(final Collection<E> dst, final Iterable<? extends E> src) {
        for (final E elem : unNull(src)) {
            dst.add(elem);
        }
    }

    /**
     * Adds all elements of the source Collection to the destination Collection
     * 
     * @param dst the destination Collection
     * @param src the source Collection
     * @param <E> the element type
     * @return whether the destination was changed as a result of the call
     **/
    public final static <E> boolean addAll(final Collection<E> dst, final Collection<E> src) {
        return (dst != null) && (src != null) ? dst.addAll(src) : false;
    }

    /**
     * Adds the given Object to the given List, allocating a new List if necessary
     * 
     * @param l the List
     * @param o the Object to add
     * @param <E> the element type
     * @return the List
     **/
    public final static <E> List<E> add(List<E> l, final E o) {
        (l = ((l == null) ? new ArrayList<E>() : l)).add(o);
        return l;
    }

    /**
     * Adds the given Object to the given List, allocating a new List if necessary
     * 
     * @param initialCapacity the initial capacity
     * @param l the List
     * @param o the Object to add
     * @param <E> the element type
     * @return the List
     **/
    public final static <E> List<E> add(final int initialCapacity, List<E> l, final E o) {
        (l = ((l == null) ? new ArrayList<E>(initialCapacity) : l)).add(o);
        return l;
    }

    /**
     * Places the given Object in the given List at the desired index, allocating if necessary
     * 
     * @param l the List
     * @param i the desired index
     * @param o the Object to add
     * @param <E> the element type
     **/
    public final static <E> List<E> set(List<E> l, final int i, final E o) {
        if (l == null) {
            l = new ArrayList<E>();
        }

        for (int j = l.size(); j <= i; j++) {
            l.add(null);
        }
        l.set(i, o);

        return l;
    }

    /**
     * Adds the given Object to the given List if the Object is not contained in it or null,
     * allocating a new List if necessary
     * 
     * @param l the List
     * @param o the Object to add
     * @param <E> the element type
     * @return the List
     **/
    public final static <E> List<E> addIfNotContainedOrNull(final List<E> l, final E o) {
        return (o != null) && !contains(l, o) ? add(l, o) : l;
    }

    /**
     * Adds the given Object to the given List if the Object is not contained in it, allocating
     * a new List if necessary
     * 
     * @param l the List
     * @param o the Object to add
     * @param <E> the element type
     * @return the List
     **/
    public final static <E> List<E> addIfNotContained(final List<E> l, final E o) {
        return !contains(l, o) ? add(l, o) : l;
    }

    /**
     * Adds the given Object to the given List if the Object is not null, allocating
     * a new List if necessary
     * 
     * @param l the List
     * @param o the Object to add
     * @param <E> the element type
     * @return the List
     **/
    public final static <E> List<E> addIfNotNull(final List<E> l, final E o) {
        return o != null ? add(l, o) : l;
    }

    /**
     * Adds the given Object to the given List if the Object is not null, allocating
     * a new List if necessary
     * 
     * @param initialCapacity the initial capacity
     * @param l the List
     * @param o the Object to add
     * @param <E> the element type
     * @return the List
     **/
    public final static <E> List<E> addIfNotNull(final int initialCapacity, final List<E> l, final E o) {
        return o != null ? add(initialCapacity, l, o) : l;
    }

    /**
     * Adds the given Object to the given Collection if the Object is not null
     * 
     * @param c the Collection
     * @param o the Object to add
     * @param <E> the element type
     **/
    public final static <E> void addIfNotNull(final Collection<E> c, final E o) {
        if (o != null) {
            c.add(o);
        }
    }

    /**
     * Adds the given Objects to the given List that are not contained in it or null,
     * allocating a new List if necessary
     * 
     * @param l the List
     * @param c the Collection of Objects to add
     * @param <E> the element type
     * @return the List
     **/
    public final static <E> List<E> addAllIfNotContainedOrNull(List<E> l, final Collection<E> c) {
        if (c != null) {
            final Iterator<E> iter = c.iterator();
            while (iter.hasNext()) {
                l = addIfNotContainedOrNull(l, iter.next());
            }
        }

        return l;
    }

    /**
     * Adds the given Objects to the given List that are not null, allocating a new List if
     * necessary
     * 
     * @param l the List
     * @param c the Collection of Objects to add
     * @param <E> the element type
     * @return the List
     **/
    public final static <E> List<E> addAllIfNotNull(List<E> l, final Collection<E> c) {
        if (c != null) {
            for (final E elem : c) {
                l = addIfNotNull(l, elem);
            }
        }

        return l;
    }

    /**
     * Given a List already sorted in accordance with a Comparator, adds a new element at the
     * appropriate index, allocating a new List if necessary
     * 
     * @param l the sorted List
     * @param o the new element
     * @param cmp the Comparator
     * @param <E> the element type
     **/
    public final static <E> List<E> add(List<E> l, final E o, final Comparator<E> cmp) {
        if (l == null) {
            l = new ArrayList<E>();
        }
        int i = l.size() - 1;
        // Search backwards; if added in right order, then only one comparison needed
        for (; i >= 0; i--) {
            if (cmp.compare(l.get(i), o) <= 0) {
                break;
            }
        }
        l.add(i + 1, o);
        return l;
    }

    public final static <E> void ensureCapacity(final Collection<E> col, final int minCapacity) {
        if (col instanceof ArrayList) {
            ((ArrayList<E>) col).ensureCapacity(minCapacity);
        }
    }

    /**
     * Retrieves an unmodifiable List backed by the given List, or null if the given List is null
     * 
     * @param l the List
     * @param <E> the element type
     * @return the unmodifiable List
     **/
    public final static <E> List<E> unmodifiableList(final List<E> l) {
        return l == null ? null : Collections.unmodifiableList(l);
    }

    /**
     * Removes nulls from the given List
     * 
     * @param list the List
     * @param <L> the List type
     * @return the List
     **/
    public final static <L extends List<?>> L trim(final L list) {
        int size = size(list);
        for (int i = 0; i < size; i++) {
            if (list.get(i) == null) {
                list.remove(i);
                i--;
                size--;
            }
        }

        return isEmpty(list) ? null : list;
    }

    /**
     * Adds the given Objects to the given List, allocating a new List if necessary
     * 
     * @param l the List
     * @param o1 the first Object to add
     * @param o2 the second Object to add
     * @param <E> the element type
     * @return the List
     **/
    public final static <E> List<E> add(final List<E> l, final E o1, final E o2) {
        return add(add(l, o1), o2);
    }

    /**
     * Removes and returns the last Object of the given List
     * 
     * @param l the List
     * @param <E> the element type
     * @return the last Object
     **/
    public final static <E> E removeLast(final List<E> l) {
        final int size = size(l);

        return size == 0 ? null : l.remove(size - 1);
    }

    /**
     * Retrieves the last Object of the given List
     * 
     * @param l the List
     * @param <E> the element type
     * @return the last Object
     **/
    public final static <E> E getLast(final List<E> l) {
        final int size = size(l);

        return size == 0 ? null : l.get(size - 1);
    }

    /**
     * Retrieves the String at the given index of the given String[] array
     * 
     * @param a the String[] array
     * @param i the index
     * @return the String
     **/
    public final static String get(final String[] a, final int i) {
        return length(a) <= i ? null : a[i];
    }

    public final static Map<String, String> toArgMap(final String[] args, final int firstKey) {
        final int argsSize = length(args);
        final Map<String, String> map = new LinkedHashMap<String, String>(Math.max(0, (argsSize - 2) / 2));
        for (int i = firstKey; i < argsSize; i++) {
            map.put(args[i].substring(1), args[++i]);
        }
        return map;
    }

    /**
     * Retrieves the first element of the given List that .equals(toFind)
     * 
     * @param list the List
     * @param toFind the Object to find
     * @param <E> the element type
     * @return the List element
     **/
    public final static <E> E get(final List<E> list, final Object toFind) {
        return list == null ? null : list.get(list.indexOf(toFind));
    }

    /**
     * Retrieves the Object at the given index of the given List
     * 
     * @param list the List
     * @param i the index
     * @param <E> the element type
     * @return the Object
     **/
    public final static <E> E get(final List<E> list, final int i) {
        return (i < 0) || (size(list) <= i) ? null : list.get(i);
    }

    /**
     * Retrieves the Object with the given key from the given Map
     * 
     * @param map the Map
     * @param key the key
     * @param <V> the value type
     * @return the Object
     **/
    public final static <V> V get(final Map<?, V> map, final Object key) {
        return map == null ? null : map.get(key);
    }

    /**
     * Retrieves the Object with the given key from the given Map, or null if it does not match the
     * expected type
     * 
     * @param map the Map
     * @param key the key
     * @param expectedValueType the Class expected value type
     * @param <V> the Map value type
     * @param <E> the expected value type
     * @return the Object
     **/
    public final static <V, E extends V> E get(final Map<?, V> map, final Object key,
            final Class<E> expectedValueType) {
        final V value = get(map, key);

        if ((value != null) && ReflectUtil.isAssignableFrom(expectedValueType, value.getClass())) {
            return cast(value);
        }

        return null;
    }

    public final static String getProperty(final Properties prop, final String key) {
        return prop == null ? null : prop.getProperty(key);
    }

    /**
     * Adds the given value to the List stored under the given key in the given Map, creating
     * a new List if needed
     * 
     * @param map the Map
     * @param key the key of the desired List
     * @param value the value
     * @param <K> the key type
     * @param <V> the value type
     **/
    public final static <K, V> void addToMapList(final Map<K, List<V>> map, final K key, final V value) {
        List<V> list = map.get(key);

        if (list == null) {
            list = new ArrayList<V>();
            map.put(key, list);
        }

        list.add(value);
    }

    /**
     * Determines if the given Iterable contains the given Object
     * 
     * @param c the Iterable
     * @param o the Object
     * @return true if c contains o, false otherwise
     **/
    public final static boolean contains(final Iterable<?> c, final Object o) {
        for (final Object i : c) {
            if (equals(i, o)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Determines if the given Collection contains the given Object
     * 
     * @param c the Collection
     * @param o the Object
     * @return true if c contains o, false otherwise
     **/
    public final static boolean contains(final Collection<?> c, final Object o) {
        return c == null ? false : c.contains(o);
    }

    /**
     * Determines if the given Map contains the given key
     * 
     * @param m the Map
     * @param o the key
     * @return true if m contains o, false otherwise
     **/
    public final static boolean containsKey(final Map<?, ?> m, final Object o) {
        return m == null ? false : m.containsKey(o);
    }

    public final static boolean containsAny(final Collection<?> c1, final Collection<?> c2) {
        for (final Object o : c2) {
            if (c1.contains(o)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Determines if the given Object is in the given array
     * 
     * @param toFind the Object to find
     * @param set the Object[] array
     * @return true the Object is found in the array
     **/
    public final static boolean in(final Object toFind, final Object... set) {
        for (final Object o : set) {
            if (equals(o, toFind)) {
                return true;
            }
        }

        return false;
    }

    public final static boolean inIgnoreCase(final String toFind, final String... set) {
        for (final String s : set) {
            if (equalsIgnoreCase(s, toFind)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Determines if the given int is in the given array
     * 
     * @param toFind the int to find
     * @param set the int[] array
     * @return true the int is found in the array
     **/
    public final static boolean in(final int toFind, final int... set) {
        for (final int o : set) {
            if (o == toFind) {
                return true;
            }
        }

        return false;
    }

    /**
     * Determines if the given array contains the given String, ignoring case
     * 
     * @param a the String[] array
     * @param s the String
     * @return true if a contains s, false otherwise
     **/
    public final static boolean containsIgnoreCase(final String[] a, final String s) {
        for (final String e : a) {
            if (equalsIgnoreCase(e, s)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Creates a Properties instance from the given array of 2-element key/value arrays
     * 
     * @param entries the array of 2-element key/value arrays
     * @return the Properties
     **/
    public final static Properties createProperties(final String[][] entries) {
        final Properties prop = new Properties();

        for (final String[] entry : entries) {
            if (entry.length != 2) {
                throw new RuntimeException("Invalid property entry: " + Arrays.asList(entry));
            }
            prop.setProperty(entry[0], entry[1]);
        }

        return prop;
    }

    /**
     * Loads the Properties
     * 
     * @param prop the Properties
     * @param location the Properties source location
     * @return the Properties
     **/
    public final static Properties loadProperties(Properties prop, final String location) {
        for (final String loc : splitExactIntoList(location, getPathSeparator())) {
            prop = loadProperties1(prop, loc);
        }
        return prop;
    }

    private final static Properties loadProperties1(Properties prop, final String location) {
        InputStream is = null;

        try {
            /*
            CORE-1439
            Properties in general aren't required.
            DataSource might be specified with a setter instead of a property.
            Could be injected by Spring.
            Most other properties have defaults or aren't required.
            */
            is = getStream(location);
            if (prop == null) {
                prop = new Properties();
            }
            if (is == null) {
                log.warn(getNotFoundMessage(location));
                return prop;
            }

            if (log.isDebugEnabled()) {
                final Properties sourceProperties = new Properties();
                sourceProperties.load(is);
                for (final String key : sourceProperties.stringPropertyNames()) {
                    if (prop.get(key) != null) {
                        log.debug("Overriding property with key: " + key);
                    }
                    prop.setProperty(key, sourceProperties.getProperty(key));
                }
            } else {
                prop.load(is);
            }

            return prop;
        } catch (final Exception e) {
            throw toRuntimeException(e);
        } finally {
            IoUtil.close(is);
        }
    }

    /**
     * Retrieves the Properties
     * 
     * @param location the Properties source location
     * @return the Properties
     **/
    public final static Properties getProperties(final String location) {
        return loadProperties(null, location);
    }

    public final static PropertyResolver registerPropertyResolver(final PropertyResolver propertyResolver) {
        if (propertyResolver == null) {
            throw new NullPointerException("Cannot register a null PropertyResolver");
        }
        final PropertyResolver old = Util.propertyResolver;
        Util.propertyResolver = propertyResolver;
        return old;
    }

    public final static PropertyResolver getPropertyResolver() {
        return propertyResolver;
    }

    public final static void setProperties(final Properties prop) {
        ((RegenPropertyResolver) propertyResolver).setProperties(prop); // Don't know what to do if we get a ClassCastException
    }

    /**
     * Retrieves the property value, searching the System properties defined as JVM arguments and
     * the global properties file
     * 
     * @param key the property key
     * @return the property value
     **/
    public final static String getProperty(final String key) {
        return propertyResolver.getProperty(key);
    }

    /**
     * Retrieves the property value or the given default
     * 
     * @param key the property key
     * @param defaultValue the default value
     * @return the property value
     **/
    public final static String getProperty(final String key, final String defaultValue) {
        return nvl(getProperty(key), defaultValue);
    }

    /**
     * Retrieves whether the given property is true
     * 
     * @param key the property's key
     * @param defaultValue the default if the property is unspecified or not a boolean value
     * @return whether the given property is true
     **/
    public final static boolean isProperty(final String key, final boolean defaultValue) {
        return toBoolean(getProperty(key), defaultValue);
    }

    /**
     * Retrieves whether the given value is true
     * 
     * @param booleanString the value
     * @param defaultValue the default if the value is unspecified or not a boolean value
     * @return whether the given value is true
     **/
    public final static boolean toBoolean(final String booleanString, final boolean defaultValue) {
        return toBoolean(booleanString, TRUE, FALSE, defaultValue);
    }

    public final static boolean toBoolean(final String in, final String tru, final String fls,
            final boolean defVal) {
        return defVal ? !fls.equalsIgnoreCase(in) : tru.equalsIgnoreCase(in);
    }

    /**
     * Retrieves the given property as an int
     * 
     * @param key the property's key
     * @param defaultValue the default if the property is unspecified or not an int value
     * @return the value
     **/
    public final static int getPropertyInt(final String key, final int defaultValue) {
        final String value = getProperty(key);
        //if (isAllDigits(value)) { // Too restrictive
        if (isValued(value)) {
            try {
                return Integer.parseInt(value);
            } catch (final Exception e) {
                // Just use defaultValue
            }
        }
        return defaultValue;
    }

    /**
     * Retrieves the given property as a long
     * 
     * @param key the property's key
     * @param defaultValue the default if the property is unspecified or not a long value
     * @return the value
     **/
    public final static long getPropertyLong(final String key, final long defaultValue) {
        final String value = getProperty(key);
        if (isValued(value)) {
            try {
                return Long.parseLong(value);
            } catch (final Exception e) {
                // Just use defaultValue
            }
        }
        return defaultValue;
    }

    /**
     * Retrieves the property value from the given Properties whose key contains the given partial
     * key
     * 
     * @param prop the Properties to search
     * @param partialKey the partial key
     * @return the property value
     **/
    public final static String getPropertyPartialKey(final Properties prop, final String partialKey) {
        if (prop == null) {
            return null;
        }

        final Iterator<?> iter = prop.keySet().iterator();
        while (iter.hasNext()) {
            final String key = (String) iter.next();
            if (key.startsWith("org.regenstrief.") && key.endsWith(partialKey)) {
                return prop.getProperty(key);
            }
        }

        return null;
    }

    /**
     * Retrieves the property value, searching the System properties defined as JVM arguments and
     * the global properties file
     * 
     * @param partialKey the partial key
     * @return the property value
     **/
    public final static String getPropertyPartialKey(final String partialKey) {
        for (final String s : new String[] { "database", "query", "queryposer" }) {
            final String key = "org.regenstrief." + s + "." + partialKey;
            if (propertyResolver.hasProperty(key)) {
                return propertyResolver.getProperty(key);
            }
        }
        return null;
    }

    /**
     * It might not be a good idea to call System.exit from our code. However, since we already do,
     * it seems good to funnel all of these through this method. That way we can add features like
     * sending an email notification to someone whenever a program exits if we want.
     * 
     * @param status the status
     **/
    public final static void exit(final int status) {
        // Might want to make sure that java.lang.SecurityManager.checkExit(int status) always throws a SecurityException on servers
        // and checkPermission with the RuntimePermission("exitVM") 
        log.info("Exiting", new Exception("Exiting"));
        System.exit(status);
    }

    /**
     * Remove null values from an array
     * 
     * @param originalArray Array
     * @param newArrayLength String
     * @return Array
     */
    public final static Object[] removeNullsFromArray(Object[] originalArray, final int newArrayLength) {
        final Object[] tempBreakdowns = new Object[newArrayLength];
        final int origLength = originalArray.length;
        int i = 0, j = 0;

        while ((i < newArrayLength) && (j < origLength)) {
            if (originalArray[j] != null) {
                tempBreakdowns[i] = originalArray[j];
                i++;
            }
            j++;
        }

        originalArray = new Object[newArrayLength];

        for (i = 0; i < newArrayLength; i++) {
            originalArray[i] = tempBreakdowns[i];
        }
        return originalArray;
    }

    /**
     * Returns i as a 2 digit string
     * 
     * @param i an integer
     * @return i as a 2 digit string
     **/
    public final static String to2DigitString(final int i) {
        return i < 10 ? "0" + i : Integer.toString(i);
    }

    /**
     * Creates a 3 digit string from an integer with leading zeros if needed
     * 
     * @param i the integer
     * @return the 3 digit String
     **/
    public final static String to3DigitString(final int i) {
        String val = Integer.toString(i);
        if (i < 100) { // IF need some leading zeroes
            val = ((i < 10) ? "00" : "0") + val;
        }
        return val;
    }

    /**
     * Creates a 4 digit string from an integer with leading zeros if needed
     * 
     * @param i the integer
     * @return the 4 digit String
     **/
    public final static String to4DigitString(final int i) {
        String num = Integer.toString(i); // 4-digit number within exchange
        if (i < 1000) { // IF need leading zeroes
            if (i < 100) { // THEN IF need more than one
                num = ((i < 10) ? "000" : "00") + num;
            } else {
                num = "0" + num;
            }
        }
        return num;
    }

    /**
     * Returns the double as a String, without a decimal point if the double actually stores an
     * integer
     * 
     * @param d the double
     * @return the double as a String
     **/
    public final static String doubleToString(final double d) {
        // If the double equals itself rounded to an integer, then it stores an integer
        final int i = (int) d;
        return i == d ? String.valueOf(i) : String.valueOf(d);
    }

    public final static String doubleToString(final Number d) {
        return d == null ? null : doubleToString(d.doubleValue());
    }

    public final static Integer toInteger(final Object o) {
        if (o == null) {
            return null;
        }
        return o instanceof Integer ? (Integer) o : Integer.valueOf(((Number) o).intValue());
    }

    public final static Long toLong(final Object o) {
        if (o == null) {
            return null;
        }
        return o instanceof Long ? (Long) o : Long.valueOf(((Number) o).longValue());
    }

    /**
     * Deletes the file with the given name
     * 
     * @param fileName the file name
     **/
    public final static void delete(final String fileName) {
        final File f = new File(fileName);
        final String sep = getFileSeparator();

        if (f.isDirectory()) {
            final String children[] = f.list(), t = fileName + sep;
            for (int i = 0; i < children.length; i++) {
                delete(t + children[i]);
            }
        }
        f.delete();
    }

    /**
     * Retrieves whether the file with the given name exists
     * 
     * @param fileName the file name
     * @return whether the file exists
     **/
    public final static boolean exists(final String fileName) {
        return fileName == null ? false : new File(fileName).exists();
    }

    /**
     * Renames the file with the given name
     * 
     * @param src the source file name
     * @param dst the destination file name
     **/
    public final static void rename(final String src, final String dst) {
        new File(src).renameTo(new File(dst));
    }

    /**
     * Reads the bytes of the InputStream
     * 
     * @param loc the location of the InputStream
     * @return the byte[] array
     * @throws Exception if an I/O problem occurs
     **/
    public final static byte[] readBytes(final String loc) throws Exception {
        final InputStream is = getStream(loc);

        try {
            return readBytes(is);
        } finally {
            is.close();
        }
    }

    /**
     * Reads the bytes of the InputStream
     * 
     * @param is the InputStream
     * @return the byte[] array
     * @throws IOException if an I/O problem occurs
     **/
    public final static byte[] readBytes(final InputStream is) throws IOException {
        byte[] buf = new byte[BUFFER_SIZE], back;
        int size = 0, amt;

        while ((amt = is.read(buf, size, BUFFER_SIZE)) != -1) {
            size += amt;
            if (buf.length - size < BUFFER_SIZE) {
                back = new byte[buf.length * 2];
                System.arraycopy(buf, 0, back, 0, size);
                buf = back;
            }
        }
        if (amt > 0) {
            size += amt;
        }

        if (size < buf.length) {
            back = new byte[size];
            System.arraycopy(buf, 0, back, 0, size);
            buf = back;
        }

        return buf;
    }

    /**
     * Reads the given File
     * 
     * @param f the File
     * @return the contents of the file
     * @throws IOException if an I/O error occurs while reading the file
     **/
    public final static String readFile(final File f) throws IOException {
        final FileInputStream in = new FileInputStream(f);
        try {
            return readStream(in);
        } finally {
            IoUtil.close(in);
        }
    }

    /**
     * Reads the file with the given name
     * 
     * @param name the file name
     * @return the contents of the file
     * @throws IOException if an I/O error occurs while reading the file
     **/
    public final static String readFile(final String name) throws IOException {
        System.out.println(" errrrr ");
        final StringBuilder sb = new StringBuilder();
        final char[] buffer = new char[BUFFER_SIZE];
        FileReader f = null;
        int i = 0;
        InputStream is;

        try {
            f = new FileReader(name);
            while (i >= 0) {
                i = f.read(buffer, 0, BUFFER_SIZE);
                if (i > 0) {
                    sb.append(buffer, 0, i);
                }
            }

            f.close();
            return sb.toString();
        } catch (final Exception e) {
        }
        is = getStreamRequired(name);
        try {
            return readStream(is);
        } finally {
            is.close();
        }
    }

    /**
     * Reads an int from the file with the given name
     * 
     * @param name the file name
     * @return the int, or -1 if an error occurred
     **/
    public final static int readFileInt(final String name) {
        String s = null;

        try {
            s = trim(readFile(name));
        } catch (final Exception e) {
        }
        if (s != null) {
            try {
                return Integer.parseInt(s);
            } catch (final Exception e) {
            }
        }

        return -1;
    }

    /**
     * Reads the contents of the given URL
     * 
     * @param url the URL
     * @return the contents of the URL
     * @throws IOException if an I/O problem occurs
     **/
    public final static String readURL(final String url) throws IOException {
        final InputStream in = getStreamFromURL(url);
        try {
            return readStream(in);
        } finally {
            IoUtil.close(in);
        }
    }

    /**
     * Reads the number of items that an importer should skip
     * 
     * @param name the file name
     * @param label the label to print if any items are to be skipped
     * @return the number of items that an importer should skip
     **/
    public final static int readSkipAmount(final String name, final String label) {
        final int skip = readFileInt(name);

        if (skip > 0) {
            log.info("Skipping " + skip + " " + label + " according to " + name);
        }

        return skip;
    }

    /**
     * Retrieves the given file name's extension
     * 
     * @param fileName the file name
     * @return the extension
     */
    public final static String getExtension(final String fileName) {
        final int dot = fileName.lastIndexOf('.');
        return dot < 0 ? null : fileName.substring(dot + 1);
    }

    /**
     * Replaces name.ext with name.label.ext
     * 
     * @param inName the input file name
     * @param label the processed label
     * @return the processed name
     */
    public final static String getProcessedFileName(final String inName, final String label) {
        final int dot = inName.lastIndexOf('.');
        return inName.substring(0, dot + 1) + label + inName.substring(dot);
    }

    /**
     * Retrieves whether the given character is in the range from '0' to '9' (more restrictive than
     * Character.isDigit)
     * 
     * @param c the character
     * @return whether the given character is in the range from '0' to '9'
     **/
    public final static boolean isDigit(final char c) {
        return (c >= '0') && (c <= '9');
    }

    /**
     * Retrieves whether all characters in the given String are in the range from '0' to '9'
     * 
     * @param s the String
     * @return whether the characters are in the range from '0' to '9'
     **/
    public final static boolean isAllDigits(final String s) {
        return (isAllDigits(s, 0, length(s)));
    }

    public final static boolean isAllDigits(final String s, final int beginIndex, final int endIndex) {
        if (endIndex <= beginIndex) {
            return false; // if isAllDigits is true, Integer.parseInt shouldn't fail
        }

        for (int i = beginIndex; i < endIndex; i++) {
            if (!isDigit(s.charAt(i))) {
                return false;
            }
        }

        return true;
    }

    /**
     * Retrieves whether the given String contains numeric data anywhere in it
     * 
     * @param s the String
     * @return whether the given String contains numeric data anywhere in it
     **/
    public final static boolean hasNumeric(final String s) {
        final int len = length(s);

        for (int i = 0; i < len; i++) {
            if (isDigit(s.charAt(i))) {
                return true;
            }
        }

        return false;
    }

    /**
     * Retrieves whether the given character is a hexidecimal value (where 10-15 are represented as
     * a-f)
     * 
     * @param c the character
     * @return whether the character is a hexidecimal value
     **/
    public final static boolean isLowercaseHex(final char c) {
        return isDigit(c) || ((c >= 'a') && (c <= 'f'));
    }

    /**
     * Converts the given bytes into a hexidecimal String (where 10-15 are represented as a-f)
     * 
     * @param bytes the bytes
     * @return the hexidecimal String
     **/
    public final static String toLowercaseHex(final byte[] bytes) {
        final int len = length(bytes);

        if (len == 0) {
            return null;
        }

        final StringBuilder sb = new StringBuilder(len * 2);
        for (int i = 0; i < len; i++) {
            final byte b = bytes[i];
            appendLowercaseHex(sb, (b >> 4) & 0x0F);
            appendLowercaseHex(sb, b & 0x0F);
        }

        return isEmpty(sb) ? "0" : sb.toString();
    }

    /**
     * Appends the given half-byte (4 bits, 0-15) onto the given StringBuilder as a hexidecimal char
     * (where 10-15 are represented as a-f)
     * 
     * @param sb the StringBuilder
     * @param halfbyte the half-byte
     **/
    private final static void appendLowercaseHex(final StringBuilder sb, final int halfbyte) {
        final char c = toLowercaseHex(halfbyte);

        if ((c != '0') || (sb.length() > 0)) {
            sb.append(c);
        }
    }

    /**
     * Converts the given half-byte (4 bits, 0-15) into a hexidecimal char (where 10-15 are
     * represented as a-f)
     * 
     * @param halfbyte the half-byte
     * @return the hexidecimal char
     **/
    public final static char toLowercaseHex(final int halfbyte) {
        return toHex('a', halfbyte);
    }

    public final static char toUppercaseHex(final int halfbyte) {
        return toHex('A', halfbyte);
    }

    private final static char toHex(final char a, final int halfbyte) {
        return (char) (halfbyte < 10 ? '0' + halfbyte : a + halfbyte - 10);
    }

    public final static String toUppercaseHexString(final char c) {
        return toUppercaseHexString((int) c);
    }

    private final static String toUppercaseHexString(final int i) {
        return "" + toUppercaseHex(i / 16) + toUppercaseHex(i % 16);
    }

    /**
     * Retrieves whether the given character is a hexidecimal value
     * 
     * @param c the character
     * @return whether the character is a hexidecimal value
     **/
    public final static boolean isHex(final char c) {
        return isDigit(c) || ((c >= 'a') && (c <= 'f')) || ((c >= 'A') && (c <= 'F'));
    }

    public final static int fromHex(final char c) {
        if ((c >= '0') && (c <= '9')) {
            return c - '0';
        } else if (c >= 'a' && c <= 'f') {
            return c - 'a' + 10;
        } else if (c >= 'A' && c <= 'F') {
            return c - 'A' + 10;
        }
        throw new IllegalArgumentException("Invalid hex character " + c);
    }

    public final static int fromHex(final char c1, final char c2) {
        return fromHex(c1) * 16 + fromHex(c2);
    }

    public final static int fromHex(final CharSequence s) {
        return fromHex(s, 0, length(s));
    }

    public final static int fromHex(final CharSequence s, final int off, final int len) {
        int r = 0;
        for (int i = off; i < len; i++) {
            r = (r * 16) + fromHex(s.charAt(i));
        }
        return r;
    }

    /**
     * Retrieves whether all characters in the given String are whitespace
     * 
     * @param s the String
     * @return whether the characters are whitespace
     **/
    public final static boolean isAllWhitespace(final String s) {
        for (int i = length(s) - 1; i >= 0; i--) {
            if (!Character.isWhitespace(s.charAt(i))) {
                return false;
            }
        }

        return true;
    }

    /**
     * Retrieves the SHA-1 hash of the given String
     * 
     * @param in the String
     * @return the SHA-1 hash
     **/
    public final static String sha1(final String in) {
        try {
            final MessageDigest md = MessageDigest.getInstance("SHA-1");

            md.update(in.getBytes());

            return toLowercaseHex(md.digest());
        } catch (final Exception e) {
            throw toRuntimeException(e);
        }
    }

    /**
     * <p>
     * Title: My Authenticator
     * </p>
     * <p>
     * Description: Creates a PasswordAuthentication.
     * </p>
     * <p>
     * Copyright: Copyright (c) 2006
     * </p>
     * <p>
     * Company: Regenstrief Institute
     * </p>
     * 
     * @author Andrew Martin
     * @version 1.0
     */
    private static class MyAuthenticator extends Authenticator {

        @Override
        protected PasswordAuthentication getPasswordAuthentication() {
            /*
             * If we access a URL that does not require authentication, this is not invoked.
             * If we access a URL that requires authentication twice, this is not invoked the second time.
             * It must use cookies or do something to keep track of different URLs,
             * because it doesn't always send the username/password after the first time.
             * If we access another URL after the first authentication,
             * and if we look at the HTTP headers sent,
             * there is no Authorization header.
             * If we then access another site requiring authentication, this is invoked again.
             */
            //getRequestingHost(); // Could use a different property for each host; also getRequestingSite()
            try {
                String user = authenticatorUser.get();
                final String password;
                if (user == null) {
                    user = System.getProperty(PROP_AUTHENTICATOR_USER);
                    password = System.getProperty(PROP_AUTHENTICATOR_PASSWORD);
                } else {
                    password = authenticatorPassword.get();
                }
                return new PasswordAuthentication(user, password.toCharArray());
            } finally {
                authenticatorUser.remove();
                authenticatorPassword.remove();
            }
        }
    }

    /**
     * Modifies the HTTP basic authentication username/password for the current thread. They will be
     * destroyed as soon as they are used.
     * 
     * @param username the username
     * @param password the password
     */
    public final static void setHttpBasicAuthentication(final String username, final String password) {
        authenticatorUser.set(username);
        authenticatorPassword.set(password);
    }

    /**
     * Stores a System property if none is currently stored for the key
     * 
     * @param key the property key
     * @param val the property value
     **/
    public final static void setPropertyIfNeeded(final String key, final String val) {
        if (System.getProperty(key) == null) {
            setProperty(key, val);
        }
    }

    /**
     * Stores a System property
     * 
     * @param key the property key
     * @param val the property value
     **/
    public final static void setProperty(final String key, final String val) {
        //set(System.getProperties(), key, val); // System.setProperty does more than System.getProperties().setProperty
        if (val != null) {
            System.setProperty(key, val);
        } else {
            System.getProperties().remove(key);
        }
    }

    public final static void set(final Properties prop, final String key, final String val) {
        if (val != null) {
            prop.setProperty(key, val);
        } else {
            prop.remove(key);
        }
    }

    /**
     * Retrieves the line separator for the current system ("\n" for Unix, "\r\n" for Windows)
     * 
     * @return the line separator
     **/
    public final static String getLineSeparator() {
        return System.getProperty("line.separator");
    }

    /**
     * Retrieves the file separator for the current system ("/" for Unix, "\" for Windows)
     * 
     * @return the file separator
     **/
    public final static String getFileSeparator() {
        return System.getProperty("file.separator");
    }

    /**
     * Retrieves the classpath separator for the current system (":" for Unix, ";" for Windows)
     * 
     * @return the classpath separator
     **/
    public final static String getPathSeparator() {
        return System.getProperty("path.separator");
    }

    /**
     * Appends the file separator to the end of the directory name if needed
     * 
     * @param dir the directory name
     * @return the formatted directory name
     **/
    public final static String formatDir(String dir) {
        if (dir == null) {
            return null;
        }

        final String sep = getFileSeparator();
        if (!dir.endsWith(sep)) {
            dir += sep;
        }

        return dir;
    }

    /**
     * Retrieves the temporary directory name
     * 
     * @return the temporary directory name
     **/
    public final static String getTempDir() {
        return formatDir(System.getProperty("java.io.tmpdir"));
    }

    /**
     * Retrieves the number of bytes remaining in the InputStream and closes it
     * 
     * @param is the InputStream
     * @return the number of bytes
     **/
    public final static long size(final InputStream is) {
        long size = 0, c;

        try {
            while (true) {
                while ((c = is.skip(BUFFER_SIZE)) > 0) { // Skip bytes
                    size += c;
                }
                if (is.read() == -1) { // EOF cannot be determined by skipping, so read
                    break;
                }
                size++; // Add a byte for the read
            }
            is.close();
        } catch (final IOException e) {
            throw new RuntimeException(e);
        }

        return size;
    }

    /**
     * Skips the desired the number of bytes (or until EOF) in the InputStream
     * 
     * @param is the InputStream
     * @param skip the number of bytes to skip
     * @return the number of bytes
     **/
    public final static long skip(final InputStream is, final long skip) {
        long size = 0, c, remaining = skip;

        try {
            do {
                while ((remaining > 0) && ((c = is.skip(remaining)) > 0)) { // Skip bytes
                    size += c;
                    remaining = skip - size;
                }
                if ((remaining == 0) || (is.read() == -1)) { // EOF cannot be determined by skipping, so read
                    break;
                }
                size++; // Add a byte for the read
                remaining = skip - size;
            } while (remaining > 0);
        } catch (final IOException e) {
            throw new RuntimeException(e);
        }

        return size;
    }

    /**
     * Retrieves the InputStream from the given location, attempting to open it by any means
     * 
     * @param location the location of the InputStream
     * @return the InputStream
     * @throws IOException if an I/O problem occurs
     **/
    public final static LabeledInputStream getStreamFromFile(final String location) throws IOException {
        return LabeledInputStream.create(location, wrapStream(new FileInputStream(location), location));
    }

    /**
     * Retrieves the InputStream from the given location, attempting to open it by any means
     * 
     * @param location the location of the InputStream
     * @return the InputStream
     * @throws IOException if an I/O problem occurs
     **/
    public final static LabeledInputStream getStreamFromURL(final String location) throws IOException {
        return LabeledInputStream.create(location, wrapStream(getRawStreamFromURL(location), location));
    }

    /**
     * Wraps the InputStream if needed for proper reading
     * 
     * @param stream the InputStream to wrap
     * @param location the location of the InputStream
     * @return the InputStream
     * @throws IOException if an I/O problem occurs
     **/
    public final static InputStream wrapStream(final InputStream stream, String location) throws IOException {
        if (stream == null) {
            return null;
        }
        location = location.toUpperCase();
        if (location.endsWith(".ZIP")) {
            final ZipInputStream zip = new ZipInputStream(stream);
            zip.getNextEntry(); // Prepares the InputStream for the first file in the .zip;
            return zip; // this method should not be used for .zip files containing more than one file
        } else if (location.endsWith(".GZ")) {
            return new GZIPInputStream(stream);
        }

        return stream;
    }

    /**
     * Retrieves the InputStream of the URLConnection, or the error InputStream if an Exception
     * occurs
     * 
     * @param con the URLConnection
     * @return the InputStream
     * @throws IOException if an I/O problem occurs
     **/
    public final static InputStream getRawStream(final URLConnection con) throws IOException {
        try {
            return con.getInputStream();
        } catch (final IOException e) {
            if (con instanceof HttpURLConnection) {
                final InputStream errStream = ((HttpURLConnection) con).getErrorStream();
                // Somtimes it's null, so throw the orignal Exception
                if (errStream != null) {
                    return errStream;
                }
            }

            throw e;
        }
    }

    /**
     * Retrieves the InputStream of the URL, or the error InputStream if an Exception occurs
     * 
     * @param url the URL
     * @return the InputStream
     * @throws IOException if an I/O problem occurs
     **/
    public final static InputStream getRawStream(final URL url) throws IOException {
        initializeSecurity();
        return getRawStream(url.openConnection());
    }

    /**
     * Initializes HTTP authentication and SSL
     **/
    public final synchronized static void initializeSecurity() {
        if (myAuthenticator == null) {
            RegenPropertyResolver.loadProperties(); // Copy from our properties file to System properties (including SSL properties)

            myAuthenticator = new MyAuthenticator(); // Enable HTTP authentication
            Authenticator.setDefault(myAuthenticator);

            //System.setProperty(PROP_HANDLER, HANDLER_SUN_SSL); // This can't be mixed with setHostnameVerifier
            //System.setProperty("javax.net.debug", "ssl:handshake"); // This can be put in our properties file

            Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
        }
    }

    public final static String post(final String url, final String content) throws IOException {
        final URLConnection ucon = new URL(url).openConnection();

        ucon.setDoInput(true);
        if (content != null) {
            ucon.setDoOutput(true);
            ucon.getOutputStream().write(content.getBytes());
        }

        return readStream(getRawStream(ucon));
    }

    /**
     * Retrieves the InputStream from the given URL, or the error InputStream if an Exception occurs
     * 
     * @param location the URL
     * @return the InputStream
     * @throws IOException if an I/O problem occurs
     **/
    public final static InputStream getRawStreamFromURL(final String location) throws IOException {
        return getRawStream(new URL(location));
    }

    /**
     * Retrieves the InputStream from the given location, attempting to open it by any means,
     * without decompressing
     * 
     * @param location the location of the InputStream
     * @return the InputStream
     **/
    public final static InputStream getRawStream(final String location) {
        InputStream stream = null;
        String s = location;
        final String sep = getFileSeparator();
        String paths[] = null;
        int n = 1;

        for (int i = 0; i < n; i++) {
            if (i > 0) {
                if (contains(paths[i - 1], sep)) {
                    paths[i - 1] = paths[i - 1].substring(0, paths[i - 1].lastIndexOf(sep) + 1);
                }
                s = paths[i - 1] + location;
            }

            try {
                stream = getRawStreamFromURL(s);
            } catch (final Exception e) {
            }
            if (stream != null) {
                break;
            }
            try {
                stream = Util.class.getResourceAsStream(s);
            } catch (final Exception e) {
            }
            if (stream != null) {
                break;
            }
            try {
                stream = Util.class.getClassLoader().getResourceAsStream(s);
                //stream = getStream(Util.class.getClassLoader(), s);
            } catch (final Exception e) {
            }
            if (stream != null) {
                break;
            }
            try {
                stream = ClassLoader.getSystemResourceAsStream(s);
            } catch (final Exception e) {
            }
            if (stream != null) {
                break;
            }
            /*try
            {   stream = getStream(ClassLoader.getSystemClassLoader(), s);
            } catch (Exception e) {}
            if (stream != null)
            {   break;
            }*/
            try {
                stream = new FileInputStream(s);
            } catch (final Exception e) {
            }
            if (stream != null) {
                break;
            }

            if (i == 0) {
                paths = getClassPath();
                n = length(paths);
                n = n > 0 ? n + 1 : n;
            }
        }

        if ((stream == null) && (location.charAt(0) != '/')) {
            return getRawStream('/' + location);
        }

        return stream;
    }

    /*private final static InputStream getStream(ClassLoader loader, final String location)
    {
       InputStream stream = null;
        
       while (loader != null)
       {
      try
      {   stream = loader.getResourceAsStream(location);
      } catch (final Exception e) {}
      if (stream != null)
      {   return stream;
      }
      loader = loader.getParent();
       }
        
       return null;
    }*/

    public final static URI toURI(final String location) {
        if (exists(location)) {
            return new File(location).toURI();
        }
        final URL url = Util.class.getClassLoader().getResource(location);
        try {
            if (url != null) {
                return url.toURI();
            }
            return new URI(location);
        } catch (final URISyntaxException e) {
            throw new RuntimeException(e);
        }
    }

    public final static URL toURL(final String location) {
        try {
            if (exists(location)) {
                return new File(location).toURI().toURL();
            }
            final URL url = Util.class.getClassLoader().getResource(location);
            if (url != null) {
                return url;
            }
            return new URL(location);
        } catch (final MalformedURLException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Retrieves the class path String
     * 
     * @return the class path String
     **/
    public final static String getClassPathString() {
        // catalina.base=C:\Program Files\Apache Software Foundation\Tomcat 5.5
        // shared.loader=${catalina.base}/shared/classes,${catalina.base}/shared/lib/*.jar
        return System.getProperty("java.class.path");
        //return concatWithDelim(System.getProperty("java.class.path"), getLoaderProperty("shared.loader"), getPathSeparator());
    }

    //private final static String getFullClassPathString() { // See CompileUtil }

    /*private final static String getLoaderProperty(final String key) {
    // Should also expand wildcards
    final String evaluated = getEvaluatedProperty(key);
    return evaluated == null ? null : replaceAllExact(evaluated, ",", getPathSeparator());
    }*/

    private final static String getEvaluatedProperty(final String key) {
        return evaluatePropertyValue(System.getProperty(key));
    }

    protected final static String evaluatePropertyValue(final String value) {
        if (value == null) {
            return null;
        }
        int bgn = 0, i = 0, dlr, len = 0;
        StringBuilder sb = null;
        while ((dlr = value.indexOf('$', i)) >= 0) {
            int opn = dlr + 1;
            if (value.charAt(opn) != '{') {
                i = opn;
                continue;
            }
            opn++;
            final int cls = value.indexOf('}', opn);
            if (cls < opn) {
                continue;
            }
            if (sb == null) {
                len = value.length();
                sb = new StringBuilder(len);
            }
            sb.append(value, bgn, dlr);
            sb.append(getEvaluatedProperty(value.substring(opn, cls)));
            bgn = i = cls + 1;
        }
        if (sb == null) {
            return value;
        }
        sb.append(value, bgn, len);
        return sb.toString();
    }

    /**
     * Retrieves the tokens of the class path
     * 
     * @return the String[] array of tokens of the class path
     **/
    public final static String[] getClassPath() {
        return splitPath(getClassPathString());
    }

    private final static String[] splitPath(final String classpath) {
        final char delim = getPathSeparator().charAt(0); //contains(classpath, ';') ? ';' : ':';

        return splitExact(classpath, delim);
    }

    public final static String[] listFiles(final String location) throws IOException {
        try {
            final File f = new File(location);
            if (f.exists() && f.isDirectory()) {
                return f.list();
            }
        } catch (final Exception e) {
            // Try another way
        }
        BufferedReader in = null;
        try {
            // Works for directories in a ClassLoader.getResource, not sure about other URLs
            in = getBufferedReader(location);
            String line;
            final List<String> files = new ArrayList<String>();
            while ((line = in.readLine()) != null) {
                files.add(line);
            }
            return files.toArray(EMPTY_ARRAY_STRING);
        } finally {
            IoUtil.close(in);
        }
    }

    /**
     * Retrieves the InputStream from the given location, attempting to open it by any means,
     * decompressing if needed
     * 
     * @param location the location of the InputStream
     * @return the InputStream
     * @throws IOException if an I/O problem occurs
     **/
    public final static LabeledInputStream getStream(final String location) throws IOException {
        return LabeledInputStream.create(location, wrapStream(getRawStream(location), location));
    }

    /**
     * Retrieves the InputStream from the given location, throwing an Exception it it is not found
     * 
     * @param location the location of the InputStream
     * @return the InputStream
     * @throws IOException if an I/O problem occurs
     **/
    public final static LabeledInputStream getStreamRequired(final String location) throws IOException {
        final LabeledInputStream in = getStream(location);

        if (in == null) {
            throw new IOException(getNotFoundMessage(location));
        }

        return in;
    }

    private final static String getNotFoundMessage(final String location) {
        return "Could not find " + location;
    }

    /**
     * Retrieves whether InputStream from the given location is available
     * 
     * @param location the location of the InputStream
     * @return the whether the InputStream is available
     **/
    public final static boolean isStreamAvailable(final String location) {
        InputStream in;

        try {
            in = getRawStream(location);
        } catch (final Exception e) {
            in = null;
        }
        try {
            return in != null;
        } finally {
            IoUtil.close(in);
        }
    }

    /**
     * Retrieves the Reader from the given location, attempting to open it by any means,
     * decompressing if needed, using the correct character encoding
     * 
     * @param location the location of the Reader
     * @return the Reader
     * @throws IOException if an I/O problem occurs
     **/
    public final static LabeledReader getReader(final String location) throws IOException {
        final LabeledInputStream in = getStream(location);

        return in == null ? null : new LabeledReader(in, getEncoding(location));
    }

    /**
     * Retrieves the Reader from the given location, attempting to open it by any means,
     * decompressing if needed, using the correct character encoding
     * 
     * @param location the location of the Reader
     * @return the Reader
     * @throws IOException if an I/O problem occurs
     **/
    public final static LabeledReader getReaderRequired(final String location) throws IOException {
        return new LabeledReader(getStreamRequired(location), getEncoding(location));
    }

    /**
     * Retrieves a Reader for the given InputStream, using the correct character encoding
     * 
     * @param is the InputStream
     * @return the Reader
     * @throws Exception if an I/O problem occurs
     **/
    public static final Reader getReader(InputStream is) throws Exception {
        is = getMarkableInputStream(is, 11);
        final String enc = getEncoding(is, false);
        is.reset();

        if (is instanceof LabeledInputStream) {
            return new LabeledReader((LabeledInputStream) is, enc);
        }
        return new BomReader(is, enc);
    }

    /**
     * Retrieves the encoding of the InputStream at the given location
     * 
     * @param location the location of the InputStream
     * @return the encoding
     * @throws IOException if an I/O problem occurs
     **/
    public final static String getEncoding(final String location) throws IOException {
        return getEncoding(getStream(location));
    }

    /**
     * Retrieves the encoding of the InputStream and closes it
     * 
     * @param is the InputStream
     * @return the encoding
     * @throws IOException if an I/O problem occurs
     **/
    public final static String getEncoding(final InputStream is) throws IOException {
        return getEncoding(is, true);
    }

    /**
     * Retrieves the encoding of the InputStream and closes it if desired
     * 
     * @param is the InputStream
     * @param close whether to close the InputStream
     * @return the encoding
     * @throws IOException if an I/O problem occurs
     **/
    public final static String getEncoding(final InputStream is, final boolean close) throws IOException {
        try {
            final int b1 = is.read(), b2 = is.read();
            String enc = null;
            boolean utf16 = true;

            /*
            http://en.wikipedia.org/wiki/Byte_order_mark
            http://docs.oracle.com/javase/6/docs/api/java/nio/charset/Charset.html
            */
            if ((b1 == 239) && (b2 == 187) && (is.read() == 191)) {
                return BomReader.ENCODING_UTF_8_BOM;
            } else if (((b1 == 255) && (b2 == 254)) || ((b1 == 254) && (b2 == 255))) {
                enc = "UTF-16";
            } else if (b2 == 0) {
                is.read();
            }
            if ((b1 == 0) || (b2 == 0)) {
                for (int i = 0; i < 10; i++) {
                    final int j = is.read();
                    if (j < 0) {
                        break;
                    } else if (j != 0) {
                        utf16 = false;
                        break;
                    }
                    is.read();
                }
                if (utf16) {
                    enc = b2 == 0 ? "UTF-16LE" : "UTF-16BE";
                }
            }
            if (enc == null) {
                enc = getDefaultEncoding();
            }
            return enc;
        } finally {
            if (close) {
                is.close();
            }
        }
    }

    /**
     * Retrieves the system's default character encoding
     * 
     * @return the system's default character encoding
     **/
    public final static String getDefaultEncoding() {
        try {
            final InputStreamReader isr = new InputStreamReader(new ByteArrayInputStream(new byte[0]));
            final String enc = isr.getEncoding();

            isr.close();
            return enc;
        } catch (final IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Retrieves the InputStream from the given location, caching first if the location is remote
     * 
     * @param location the location of the InputStream
     * @return the InputStream
     * @throws Exception if an I/O problem occurs
     **/
    public final static InputStream getCachedStream(final String location) throws Exception {
        if (location.startsWith("http")) {
            final String fileName = toFileName(location);
            downloadStream(fileName, location);
            return getStream(fileName);
        } else {
            return getStream(location);
        }
    }

    /**
     * Retrieves the Reader from the given location, caching first if the location is remote
     * 
     * @param location the location of the Reader
     * @return the Reader
     * @throws Exception if an I/O problem occurs
     **/
    public final static Reader getCachedReader(final String location) throws Exception {
        return new BomReader(getCachedStream(location), getEncoding(getCachedStream(location)));
    }

    /**
     * Downloads the contents of an InputStream
     * 
     * @param fileName the destination
     * @param location the source
     * @throws Exception if an I/O problem occurs
     **/
    public final static void downloadStream(final String fileName, final String location) throws Exception {
        InputStream is = null;
        final String partialFileName = fileName + ".par";
        int skip = 0;
        boolean needDownload = true;

        try {
            is = getStream(fileName);
        } catch (final Exception e) {
        }
        if (is != null) {
            is.close();
            return;
        }
        log.info("Downloading " + location + " to " + fileName);
        while (needDownload) {
            try {
                is = getStream(partialFileName);
            } catch (final Exception e) {
            }
            if (is != null) {
                skip = is.available();
                is.close();
            }
            is = getRawStream(location);
            try {
                if (skip > 0) {
                    skip(is, skip);
                }
                bufferedReadWrite(is, new FileOutputStream(partialFileName, true));
                needDownload = false; // Break loop if there's no Exception & this is reached
            } catch (final Exception e) {
            }
        }
        rename(partialFileName, fileName);
        log.info("Done downloading");
    }

    /**
     * Reads the given InputStream
     * 
     * @param in the InputStream
     * @return the contents of the InputStream
     **/
    public final static String readStream(final InputStream in) {
        final StringBuilder sb = new StringBuilder();
        final byte[] buffer = new byte[BUFFER_SIZE];
        int i = 0;

        try {
            while (i >= 0) {
                i = in.read(buffer, 0, BUFFER_SIZE);
                if (i > 0) {
                    sb.append(new String(buffer, 0, i));
                }
            }
        } catch (final Exception e) {
            throw new RuntimeException(e);
        }
        return sb.toString();
    }

    /**
     * Reads the given Reader
     * 
     * @param in the Reader
     * @return the contents of the InputStream
     **/
    public final static String readReader(final Reader in) {
        final StringBuilder sb = new StringBuilder();
        final char[] buffer = new char[BUFFER_SIZE];
        int i = 0;

        try {
            while (i >= 0) {
                i = in.read(buffer, 0, BUFFER_SIZE);
                if (i > 0) {
                    sb.append(buffer, 0, i);
                }
            }
        } catch (final Exception e) {
            throw new RuntimeException(e);
        }
        return sb.toString();
    }

    /**
     * Provides an easy way to write from a Reader to a Writer with a buffer
     * 
     * @param input Reader content to be written
     * @param output Writer place to write content
     * @param bufferSize int size of data buffer
     * @param closeOutput whether the output Writer should be closed when finished
     * @return the number of bytes that were copied
     * @throws IOException if an I/O problem occurs while reading or writing
     **/
    public static int bufferedReadWrite(final Reader input, final Writer output, final int bufferSize,
            final boolean closeOutput) throws IOException {
        int bytesRead, totalRead = 0;
        final char[] buff = new char[bufferSize];

        while (-1 != (bytesRead = input.read(buff, 0, buff.length))) {
            output.write(buff, 0, bytesRead);
            totalRead += bytesRead;
            output.flush();
        }

        input.close();
        if (closeOutput) {
            output.close();
        }
        return totalRead;
    }

    /**
     * Provides an easy way to write from a Reader to a Writer with a buffer
     * 
     * @param input Reader content to be written
     * @param output Writer place to write content
     * @param closeOutput whether the output Writer should be closed when finished
     * @return the number of bytes that were copied
     * @throws IOException if an I/O problem occurs while reading or writing
     **/
    public static int bufferedReadWrite(final Reader input, final Writer output, final boolean closeOutput)
            throws IOException {
        return bufferedReadWrite(input, output, BUFFER_SIZE, closeOutput);
    }

    /**
     * Provides an easy way to write from a Reader to a Writer with a buffer
     * 
     * @param input Reader content to be written
     * @param output Writer place to write content
     * @return the number of bytes that were copied
     * @throws IOException if an I/O problem occurs while reading or writing
     **/
    public static int bufferedReadWrite(final Reader input, final Writer output) throws IOException {
        return bufferedReadWrite(input, output, BUFFER_SIZE, true);
    }

    /**
     * Provides an easy way to write from a Reader to a Writer with a buffer
     * 
     * @param input Reader content to be written
     * @param output Writer place to write content
     * @param bufferSize int size of data buffer
     * @return the number of bytes that were copied
     * @throws IOException if an I/O problem occurs while reading or writing
     **/
    public static int bufferedReadWrite(final Reader input, final Writer output, final int bufferSize)
            throws IOException {
        return bufferedReadWrite(input, output, bufferSize, true);
    }

    /**
     * Provides an easy way to write from an InputStream to an OutputStream with a buffer
     * 
     * @param input InputSteram content to be written
     * @param output OutputStream place to write content
     * @param closeOutput whether the OutputStream should be closed
     * @return the number of bytes that were copied
     * @throws IOException if an I/O problem occurs while reading or writing
     **/
    public static int bufferedReadWrite(final InputStream input, final OutputStream output,
            final boolean closeOutput) throws IOException {
        return bufferedReadWrite(input, output, BUFFER_SIZE, closeOutput);
    }

    /**
     * Provides an easy way to write from an InputStream to an OutputStream with a buffer
     * 
     * @param input InputSteram content to be written
     * @param output OutputStream place to write content
     * @return the number of bytes that were copied
     * @throws IOException if an I/O problem occurs while reading or writing
     **/
    public static int bufferedReadWrite(final InputStream input, final OutputStream output) throws IOException {
        return bufferedReadWrite(input, output, BUFFER_SIZE, true);
    }

    /**
     * Provides an easy way to write from an InputStream to an OutputStream with a buffer
     * 
     * @param input InputSteram content to be written
     * @param output OutputStream place to write content
     * @param bufferSize int size of data buffer
     * @return the number of bytes that were copied
     * @throws IOException if an I/O problem occurs while reading or writing
     **/
    public static int bufferedReadWrite(final InputStream input, final OutputStream output, final int bufferSize)
            throws IOException {
        return bufferedReadWrite(input, output, bufferSize, true);
    }

    /**
     * Converts a String to an InputStream
     * 
     * @param s the String
     * @return the InputStream
     **/
    public final static InputStream toStream(final String s) {
        return new ByteArrayInputStream(s.getBytes());
    }

    /**
     * Reads the desired resource using the given Object's ClassLoader
     * 
     * @param o the Object whose ClassLoader should be used to read the resource
     * @param resourceName the name of the resource to read
     * @return the contents of the resource
     * @throws Exception if an I/O problem occurs
     **/
    public final static String readResource(final Object o, final String resourceName) throws Exception {
        return readStream(o.getClass().getResourceAsStream(resourceName));
    }

    /**
     * Reads the desired resource
     * 
     * @param resourceName the name of the resource to read
     * @return the contents of the resource
     * @throws Exception if an I/O problem occurs
     **/
    public final static String readResource(final String resourceName) throws Exception {
        return readStream(Class.class.getResourceAsStream(resourceName));
    }

    /**
     * Writes to the file with the given name
     * 
     * @param name the file name
     * @param s the contents of the file
     * @throws IOException if an I/O problem occurs
     **/
    public final static void writeFile(final String name, final String s) throws IOException {
        writeFile(name, s, false);
    }

    /**
     * Writes to the file with the given name
     * 
     * @param name the file name
     * @param s the contents of the file
     * @throws IOException if an I/O problem occurs
     **/
    public final static void appendFile(final String name, final String s) throws IOException {
        writeFile(name, s, true);
    }

    /**
     * Writes to the file with the given name
     * 
     * @param name the file name
     * @param s the contents of the file
     * @param append whether data should be appended to the end of the file instead of overwriting
     *            it
     * @throws IOException if an I/O problem occurs
     **/
    public final static void writeFile(final String name, final String s, final boolean append) throws IOException {
        //writer = new BufferedWriter(new FileWriter(name, append));
        final BufferedWriter writer = new BufferedWriter(getFileWriter(name, append));
        writer.write(s);
        writer.close();
    }

    private final static boolean DEF_APPEND = false;

    public final static Writer getFileWriter(final String absolutePath) throws IOException {
        return getFileWriter(absolutePath, DEF_APPEND);
    }

    public final static Writer getFileWriter(final String absolutePath, final boolean append) throws IOException {
        return new OutputStreamWriter(getFileOutputStream(absolutePath, append));
    }

    public final static Writer getFileWriter(final File f) throws IOException {
        return getFileWriter(f, DEF_APPEND);
    }

    public final static Writer getFileWriter(final File f, final boolean append) throws IOException {
        return new OutputStreamWriter(getFileOutputStream(f, append));
    }

    /**
     * Retrieves a OutputStream for the given absolute path, creating any necessary directories
     * 
     * @param absolutePath the absolute path
     * @return the OutputStream
     * @throws IOException if an I/O problem occurs
     **/
    public final static OutputStream getFileOutputStream(final String absolutePath) throws IOException {
        return getFileOutputStream(absolutePath, DEF_APPEND);
    }

    /**
     * Retrieves a OutputStream for the given absolute path, creating any necessary directories
     * 
     * @param absolutePath the absolute path
     * @param append whether the file should be opened in append mode
     * @return the OutputStream
     * @throws IOException if an I/O problem occurs
     **/
    public final static OutputStream getFileOutputStream(final String absolutePath, final boolean append)
            throws IOException {
        return getFileOutputStream(new File(absolutePath), append);
    }

    public final static OutputStream getFileOutputStream(final File f) throws IOException {
        return getFileOutputStream(f, DEF_APPEND);
    }

    public final static OutputStream getFileOutputStream(final File f, final boolean append) throws IOException {
        final File dir = f.getParentFile();

        if ((dir != null) && !dir.exists()) {
            dir.mkdirs();
        }

        return new FileOutputStream(f, append);
    }

    /**
     * Prints stack trace
     **/
    public final static void printStackTrace() {
        log.error("", new Exception());
    }

    /**
     * Retrieves the String value of the Throwable along with its stack trace
     * 
     * @param e the Throwable
     * @return the String value of the Throwable along with its stack trace
     **/
    public final static String getStackTraceString(final Throwable e) {
        if (e == null) {
            return null;
        }
        final ByteArrayOutputStream st = new ByteArrayOutputStream();
        e.printStackTrace(new PrintStream(st));
        return unNull(e.getMessage()) + getLineSeparator() + st.toString();
    }

    public final static String getStackTraceString(final Throwable e, final int maxLength) {
        final String s = getStackTraceString(e);
        if (length(s) <= maxLength) {
            return s;
        }
        return truncate(getAbbreviatedStackTraceString(e), maxLength);
    }

    public final static String getAbbreviatedStackTraceString(Throwable e) {
        /*
        Abbreviates package names and removes unnecessary content.
        Doesn't remove lines from the stack or causes.
        */
        final StringBuilder trace = new StringBuilder();
        final char br = '\n';
        StackTraceElement[] causedElems = null;
        while (e != null) {
            if (trace.length() > 0) {
                trace.append(br).append("By: ");
            }
            trace.append(replaceAllExact(abbreviateClass(e.toString()), "NullPointerException", "NPE"));
            final StackTraceElement[] elems = e.getStackTrace();
            int m = elems.length - 1, n = (causedElems == null) ? -1 : (causedElems.length - 1);
            while ((m >= 0) && (n >= 0) && elems[m].equals(causedElems[n])) {
                m--;
                n--;
            }
            for (int i = 0; i <= m; i++) {
                appendSeparator(trace, br).append(abbreviateClass(elems[i].toString()));
            }
            e = e.getCause();
            causedElems = elems;
        }
        return trace.toString();
    }

    public final static String abbreviateClass(String className) {
        // Abbreviate packages
        className = abbreviatePackage(className, "org", "regenstrief", "database", "util");
        className = abbreviatePackage(className, "org", "regenstrief", "database");
        className = abbreviatePackage(className, "org", "regenstrief", "util");
        className = abbreviatePackage(className, "org", "regenstrief");
        className = abbreviatePackage(className, "org");
        className = abbreviatePackage(className, "java", "lang");
        className = abbreviatePackage(className, "java", "util");
        className = abbreviatePackage(className, "java");
        className = abbreviatePackage(className, "sun");
        className = abbreviatePackage(className, "com");
        // Remove duplicate class name if method is on the instance's class instead of a superclass
        final int paren = className.indexOf('(');
        if (paren > 0) {
            final int dot = className.indexOf(".java:", paren + 1);
            if (dot > 0) {
                //className = className.substring(0, paren + 1) + className.substring(dot + 6); // method(line)
                className = className.substring(0, paren) + '.' + className.substring(dot + 6); // method.line)
                final int closed = className.indexOf(')', paren);
                if (closed > 0) {
                    className = className.substring(0, closed) + className.substring(closed + 1); // method.line
                }
            }
        }
        return className;
    }

    public final static String abbreviatePackage(final String className, final String... packagePath) {
        final StringBuilder path = new StringBuilder(), abbrev = new StringBuilder();
        for (final String elem : packagePath) {
            path.append(elem);
            abbrev.append(elem.charAt(0));
            // Add dot on end to make sure we don't accidentally replace javax with j, etc.
            path.append('.');
            abbrev.append('.');
        }
        return replaceAllExact(className, path.toString(), abbrev.toString());
    }

    /**
     * Retrieves text from the given Throwable
     * 
     * @param e the Throwable from which to extract text
     * @param mode determines the amount of text to retrieve
     * @return the text
     **/
    public final static String getExceptionText(final Throwable e, final int mode) {
        switch (mode) {
        case EXCEPTION_NONE:
            return null;
        case EXCEPTION_GET_MESSAGE:
            return e == null ? null : e.getMessage();
        case EXCEPTION_TO_STRING:
            return toString(e);
        case EXCEPTION_STACK_TRACE:
            return getStackTraceString(e);
        case EXCEPTION_CAUSE_STRINGS:
            return e == null ? null : concat(e.toString(), getExceptionText(e.getCause()));
        }

        return null;
    }

    /**
     * Retrieves text from the given Throwable
     * 
     * @param e the Throwable from which to extract text
     * @return the text
     **/
    public final static String getExceptionText(final Throwable e) {
        return getExceptionText(e, getPropertyInt(PROP_EXCEPTION_REPORTING, EXCEPTION_CAUSE_STRINGS));
    }

    /**
     * Creates an Exception from the Throwable if it isn't already an Exception
     * 
     * @param e the Throwable
     * @return the Exception
     **/
    public final static Exception toException(final Throwable e) {
        return e instanceof Exception ? (Exception) e : new Exception(e);
    }

    /**
     * Creates a RuntimeException from the Throwable if it isn't already a RuntimeException
     * 
     * @param e the Throwable
     * @return the RuntimeException
     **/
    public final static RuntimeException toRuntimeException(final Throwable e) {
        if (e == null) {
            // Without this check, toRuntimeException(e.getCause()) creates a confusing RuntimeException if cause is null
            return new NullPointerException("Problem without an Exception");
        }
        return e instanceof RuntimeException ? (RuntimeException) e : new RuntimeException(e);
    }

    /**
     * Creates an IOException from the Throwable if it isn't already an IOException
     * 
     * @param e the Throwable
     * @return the IOException
     **/
    public final static IOException toIOException(final Throwable e) {
        return (e instanceof IOException) ? (IOException) e : new IOException(e);
    }

    /**
     * Retrieves the index of the given char in the given String, ignoring their cases
     * 
     * @param s the String
     * @param c the char
     * @return the index
     **/
    public final static int indexOfIgnoreCase(final String s, final char c) {
        return CharSequences.indexOfIgnoreCase(s, c);
    }

    /**
     * Determines if the source contains the substring
     * 
     * @param src the source
     * @param sub the substring
     * @return true if the source contains the substring
     **/
    public final static boolean contains(final String src, final String sub) {
        return (src == null) || (sub == null) ? false : src.indexOf(sub) >= 0;
    }

    /**
     * Determines if the source starts with the substring
     * 
     * @param src the source
     * @param sub the substring
     * @return true if the source starts with the substring
     **/
    public final static boolean startsWith(final String src, final String sub) {
        return (src == null) || (sub == null) ? false : src.startsWith(sub);
    }

    /**
     * Retrieves the index of the nth occurrence of the substring in the source (0 being the first)
     * 
     * @param src the source
     * @param sub the substring
     * @param n the number of the desired occurrence
     * @return the index of the nth occurrence of the substring in the source
     **/
    public final static int nthIndexOf(final String src, final String sub, final int n) {
        int i = -1;

        for (int c = 0; c < n; c++) {
            i = src.indexOf(sub, i + 1);
            if (i == -1) {
                return i;
            }
        }

        return src.indexOf(sub, i + 1);
    }

    /**
     * Retrieves the number of occurrences of the given character
     * 
     * @param src the source CharSequence
     * @param c the character
     * @return the number of occurrences
     **/
    public final static int count(final CharSequence src, final char c) {
        final int size = length(src);
        int count = 0;
        for (int i = 0; i < size; i++) {
            if (src.charAt(i) == c) {
                count++;
            }
        }
        return count;
    }

    /**
     * Determines if the source contains the character
     * 
     * @param src the source
     * @param c the character
     * @return true if the source contains the character
     **/
    public final static boolean contains(final String src, final char c) {
        return src == null ? false : src.indexOf(c) >= 0;
    }

    /**
     * Reverses the String
     * 
     * @param s the String
     * @return the reversed String
     **/
    public final static String reverse(final String s) {
        return s == null ? null : new StringBuilder(s).reverse().toString();
    }

    /**
     * Trims a String, returning null if it is empty
     * 
     * @param s the String to trim
     * @return the trimmed String
     **/
    public final static String trim(String s) {
        return isEmpty(s = s == null ? null : s.trim()) ? null : s;
    }

    /**
     * Removes leading zeros from a String
     * 
     * @param s the String
     * @return the String without leading zeros or spaces
     **/
    public final static String trimLeadingZeros(final String s) {
        return trimLeadingCharacters(s, '0');
    }

    /**
     * Removes leading instances of the given character from a String
     * 
     * @param s the String
     * @param c the character to remove
     * @return the String without leading instances of the given character or spaces
     **/
    public final static String trimLeadingCharacters(final String s, final char c) {
        if (s == null) {
            return null;
        }
        final int sLen = s.length(); // Find initial size of string
        for (int i = 0; i < sLen; i++) {
            final char oneChar = s.charAt(i);
            if ((oneChar > ' ') && (oneChar != c)) { // Discard spaces/control chars/0s at beginning of string
                if (i == 0) {
                    return s;
                }
                return s.substring(i); // ELSE return string starting with desired char
            }
        }
        return null; // It was all "0"s if get here
    }

    /**
     * Adds leading 0s to a String if needed to ensure its length is at least n
     * 
     * @param s the String
     * @param n the desired minimum length for the String
     * @return the zero-padded String
     **/
    public final static String fillLeadingZeros(final String s, final int n) {
        return fillLeadingCharacters(s, n, '0');
    }

    /**
     * Adds leading characters to a String if needed to ensure its length is at least n
     * 
     * @param s the String
     * @param n the desired minimum length for the String
     * @param c the character to use for padding
     * @return the zero-padded String
     **/
    public final static String fillLeadingCharacters(final String s, final int n, final char c) {
        if (s == null) {
            return null;
        }
        final int len = s.length();
        if (len >= n) {
            return s;
        }

        final StringBuilder sb = new StringBuilder(n);
        for (int i = 0; i < n - len; i++) {
            sb.append(c);
        }
        sb.append(s);

        return sb.toString();
    }

    /**
     * Retrieves the length of the CharSequence
     * 
     * @param s the CharSequence
     * @return the length
     **/
    public final static int length(final CharSequence s) {
        return s == null ? 0 : s.length();
    }

    /**
     * Retrieves whether the given CharSequence has any content
     * 
     * @param s the CharSequence
     * @return whether the given CharSequence has any content
     **/
    public final static boolean isValued(final CharSequence s) {
        return length(s) > 0;
    }

    /**
     * Retrieves whether the given CharSequence is empty or null
     * 
     * @param s the CharSequence
     * @return whether the given CharSequence is empty or null
     */
    public final static boolean isEmpty(final CharSequence s) {
        return length(s) == 0;
    }

    /**
     * Retrieves the index of the first different character between two CharSequences
     * 
     * @param s1 the first CharSequence
     * @param s2 the second CharSequence
     * @return the index of the first different character (or -1 if they are identical)
     **/
    public final static int diff(final CharSequence s1, final CharSequence s2) {
        final int len1 = s1.length(), len2 = s2.length(), len = Math.min(len1, len2);

        for (int i = 0; i < len; i++) {
            if (s1.charAt(i) != s2.charAt(i)) {
                return i;
            }
        }

        return len1 == len2 ? -1 : len;
    }

    /**
     * Finds differences between two Readers
     * 
     * @param r1 the first Reader
     * @param r2 the second Reader
     * @param n the number of different characters to retrieve
     * @return the String[] array starting at the first different character
     **/
    public final static String[] diff(final Reader r1, final Reader r2, final int n) {
        final BufferedReader b1 = getBufferedReader(r1), b2 = getBufferedReader(r2);
        int d1, d2;
        boolean someMatch = false;

        try {
            while (true) {
                d1 = b1.read();
                d2 = b2.read();
                if ((d1 == -1) && (d2 == -1)) {
                    return null;
                }
                if (d1 != d2) {
                    break;
                }
                someMatch = true;
            }
            return new String[] { postDiff(d1, n, someMatch, b1), postDiff(d2, n, someMatch, b2) };
        } catch (final Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Does postprocessing after a difference is found
     * 
     * @param d the first different character
     * @param n the number of characters to retrieve
     * @param someMatch whether some characters matched
     * @param b the BufferedReader from which to retrieve characters
     * @return the String of n characters starting with the first difference
     * @throws IOException if a problem occurs
     **/
    private final static String postDiff(int d, final int n, final boolean someMatch, final BufferedReader b)
            throws IOException {
        if (d >= 0) {
            final StringBuilder s = new StringBuilder(n);
            s.append('<');
            if (someMatch) {
                s.append("...");
            }
            for (int i = 0; (i < n) && (d != -1); i++) {
                if (d >= 0) {
                    s.append((char) d);
                }
                d = b.read();
            }
            if (d >= 0) {
                s.append("...");
            }
            s.append('>');
            return s.toString();
        }

        return null;
    }

    /**
     * Appends the given character to the given StringBuffer, allocating the StringBuffer if
     * necessary
     * 
     * @param sb the StringBuffer
     * @param c the char
     * @return the StringBuffer
     **/
    public final static StringBuffer append(final StringBuffer sb, final char c) {
        return (sb == null ? new StringBuffer() : sb).append(c);
    }

    /**
     * Appends the given String to the given StringBuffer, allocating the StringBuffer if necessary
     * 
     * @param sb the StringBuffer
     * @param s the String
     * @return the StringBuffer
     **/
    public final static StringBuffer append(final StringBuffer sb, final String s) {
        return s == null ? sb : (sb == null ? new StringBuffer() : sb).append(s);
    }

    /**
     * Appends part of the given CharSequence to the given StringBuffer, allocating the StringBuffer
     * if necessary
     * 
     * @param sb the StringBuffer
     * @param s the String
     * @param offset the offset of the CharSequence at which to start copying
     * @param len the number of characters to copy
     * @return the StringBuffer
     **/
    public final static StringBuffer append(StringBuffer sb, final CharSequence s, int offset, int len) {
        if (sb == null) {
            sb = new StringBuffer(len);
        }
        sb.ensureCapacity(sb.length() + len);
        for (len += offset; offset < len; offset++) {
            sb.append(s.charAt(offset));
        }

        return sb;
    }

    /**
     * Appends the given character to the given StringBuilder, allocating the StringBuilder if
     * necessary
     * 
     * @param sb the StringBuilder
     * @param c the char
     * @return the StringBuilder
     **/
    public final static StringBuilder append(final StringBuilder sb, final char c) {
        return ((sb == null) ? new StringBuilder() : sb).append(c);
    }

    /**
     * Appends the given String to the given StringBuilder, allocating the StringBuilder if necessary
     * 
     * @param sb the StringBuilder
     * @param s the String
     * @return the StringBuilder
     **/
    public final static StringBuilder append(final StringBuilder sb, final String s) {
        return (s == null) ? sb : ((sb == null) ? new StringBuilder() : sb).append(s);
    }

    public final static StringBuilder appendSeparator(final StringBuilder sb, final char c) {
        return (sb.length() > 0) ? sb.append(c) : sb;
    }

    /**
     * Retrieves an int from a Map
     * 
     * @param map the Map
     * @param key the key Object
     * @return the int
     **/
    public final static int getInt(final Map<?, ?> map, final Object key) {
        return ((Number) map.get(key)).intValue();
    }

    /**
     * Retrieves an long from a Map
     * 
     * @param map the Map
     * @param key the key Object
     * @return the long
     **/
    public final static long getLong(final Map<?, ?> map, final Object key) {
        return ((Number) map.get(key)).longValue();
    }

    /**
     * Retrieves a boolean from a Map
     * 
     * @param map the Map
     * @param key the key Object
     * @return the boolean
     **/
    public final static boolean getBoolean(final Map<?, ?> map, final Object key) {
        return ((Boolean) map.get(key)).booleanValue();
    }

    /**
     * Retrieves a String from a Map
     * 
     * @param map the Map
     * @param key the key Object
     * @return the String
     **/
    public final static String getString(final Map<?, ?> map, final Object key) {
        return toString(map == null ? null : map.get(key));
    }

    /**
     * Retrieves the ResourceBundle
     * 
     * @param location the ResourceBundle source location
     * @return the ResourceBundle
     **/
    public final static ResourceBundle getResourceBundle(final String location) {
        FileInputStream propin = null;

        try {
            propin = new FileInputStream(location);

            return new PropertyResourceBundle(propin);
        } catch (final Exception e) {
            IoUtil.close(propin);

            return ResourceBundle.getBundle(location);
        } finally {
            IoUtil.close(propin);
        }
    }

    /**
     * Creates a HashMap containing the same data as the given ResourceBundle; can be useful because
     * HashMap's get() method is faster than ResourceBundle's getString() method when the key is not
     * found, because HashMap returns null, while ResourceBundle throws an Exception
     * 
     * @param rb the ResourceBundle
     * @return the HashMap
     **/
    public final static HashMap<String, String> resourceBundleToHashMap(final ResourceBundle rb) {
        final Enumeration<?> e = rb.getKeys();
        final HashMap<String, String> hm = new HashMap<String, String>();

        while (e.hasMoreElements()) {
            final String key = e.nextElement().toString();
            hm.put(key, rb.getString(key));
        }

        return hm;
    }

    /**
     * Creates a Map<String, String> containing the same data as the given Map
     * 
     * @param m the Map
     * @return the Map<String, String>
     **/
    public final static Map<String, String> mapToStringMap(final Map<?, ?> m) {
        final Map<String, String> hm = new HashMap<String, String>(m.size());

        putAll(hm, m);

        return hm;
    }

    /**
     * Put all entries from the source Map into the destination Map, converting keys and values to
     * Strings if needed
     * 
     * @param dst the destination Map
     * @param src the source Map
     * @param <K> the source key type
     * @param <V> the source value type
     **/
    public final static <K, V> void putAll(final Map<String, String> dst, final Map<K, V> src) {
        final Iterator<Entry<K, V>> iter = src.entrySet().iterator();

        while (iter.hasNext()) {
            final Entry<K, V> entry = iter.next();
            dst.put(toString(entry.getKey()), toString(entry.getValue()));
        }
    }

    /**
     * Gets the first chunk of s of no more than maxSize characters, splitting at delim if possible
     * 
     * @param s the String for which to get the first chunk
     * @param maxSize the maximum size of a chunk
     * @param delim the delimiter at which to split
     * @return String
     **/
    private final static String getFirstChunk(final String s, final int maxSize, final String delim) {
        final int len = length(s);
        if ((len == 0) || (maxSize < 1) || (delim == null)) {
            return null;
        } else if (len <= maxSize) {
            return s;
        }

        final int delimSize;
        int splitPoint = s.lastIndexOf(delim, maxSize); // Search for the delimiter
        if (splitPoint >= 0) {
            delimSize = delim.length();
        } else {
            splitPoint = s.lastIndexOf('\n', maxSize); // line break
            if (splitPoint >= 0) {
                delimSize = 1;
            } else {
                splitPoint = s.lastIndexOf(' ', maxSize); // space
                if (splitPoint >= 0) {
                    delimSize = 1;
                } else {
                    splitPoint = maxSize; // Use max if no delimiter found
                    delimSize = 0;
                }
            }
        }

        if (splitPoint + delimSize <= maxSize) {
            splitPoint += delimSize;
        }

        return s.substring(0, splitPoint);
    }

    /**
     * Converts a List of Strings to a String[] array
     * 
     * @param tokens the List
     * @return the String[] array
     **/
    public final static String[] toStringArray(final List<String> tokens) {
        if (tokens == null) {
            return null;
        }
        final int size = tokens.size();
        final String[] ret = new String[size];

        for (int i = 0; i < size; i++) {
            ret[i] = tokens.get(i);
        }

        return ret;
    }

    /**
     * Splits s into chunks of no more than maxSize characters, splitting at delim if possible
     * 
     * @param s the String for which to split into chunks
     * @param maxSize the maximum size of a chunk
     * @param delim the delimiter at which to split
     * @return String
     **/
    public final static String[] split(String s, final int maxSize, final String delim) {
        final int delimSize = length(delim);

        if ((s == null) || (maxSize < 1) || (delimSize == 0)) {
            return null;
        }

        final List<String> tokens = new ArrayList<String>();
        String token = getFirstChunk(s, maxSize, delim);
        while (token != null) {
            tokens.add(token);
            s = s.substring(token.length());
            // Don't want to do this when splitting OBX or NTE text; would we ever?
            /*if (s.length() >= delimSize && delim.equals(s.substring(0, delimSize)))
            {   s = s.substring(delimSize);
            }
            else if (s.length() >= 1 && (s.charAt(0) == ' ' || s.charAt(0) == '\n'))
            {   s = s.substring(1);
            }*/
            token = getFirstChunk(s, maxSize, delim);
        }

        return toStringArray(tokens);
    }

    /**
     * Splits a fixed width line
     * 
     * @param s the line
     * @param widths the widths
     * @return the tokens
     **/
    public final static String[] splitFixedWidth(final String s, final int[] widths) {
        final List<String> list = new ArrayList<String>(widths.length);
        int curr = 0;
        final int len = s.length();

        for (int i = 0; i < widths.length; i++) {
            if (curr >= len) {
                break;
            }
            final int next = Math.min(curr + widths[i], len);
            list.add(s.substring(curr, next));
            curr = next;
        }

        return toStringArray(list);
    }

    /**
     * Splits a fixed width line
     * 
     * @param s the line
     * @param widths the widths
     * @return the tokens
     **/
    public final static String[] splitFixedWidth(final String s, final String widths) {
        final List<String> list = new ArrayList<String>();
        int i, curr = 0;
        final int len = widths.length();

        for (i = 0; i < len; i++) {
            final char c = widths.charAt(i);
            if ((c < '0') || (c > '9')) {
                if (i > curr) {
                    list.add(widths.substring(curr, i));
                }
                curr = i + 1;
            }
        }
        if (i > curr) {
            list.add(widths.substring(curr, i));
        }
        final int[] tokens = new int[list.size()];
        for (i = 0; i < tokens.length; i++) {
            tokens[i] = Integer.parseInt(list.get(i));
        }

        return splitFixedWidth(s, tokens);
    }

    /**
     * Retrieves the default input delimiter character
     * 
     * @return the delimiter character
     **/
    public final static char getInputDelim() {
        return getDelim(getProperty(PROP_INPUT_DELIMITER));
    }

    /**
     * Retrieves whether the default input delimiter character has been specified
     * 
     * @return whether the default input delimiter character has been specified
     **/
    public final static boolean isInputDelimSpecified() {
        return getProperty(PROP_INPUT_DELIMITER) != null;
    }

    /**
     * Parses the given delimiter String into a delimiter character (either the 1st character of the
     * String or '\t' if the String is "tab".
     * 
     * @param delimString the delimiter String
     * @return the delimiter character
     **/
    public final static char getDelim(final String delimString) {
        return "tab".equalsIgnoreCase(delimString) ? '\t' : delimString.charAt(0);
    }

    /**
     * Splits a delimited line, a comma delimiter indicating a CSV file requiring other escaping,
     * other delimiters handled simply
     * 
     * @param s the line
     * @param delim the delimiter char
     * @return the tokens
     **/
    public final static String[] splitParse(final String s, final char delim) {
        return delim == ',' ? splitCSV(s) : splitExact(s, delim);
    }

    /**
     * Escapes a String for use as a token within a line of a CSV file
     * 
     * @param s the String
     * @return the escaped String
     **/
    public final static String escapeCSV(String s) {
        final boolean needEscape;
        if (contains(s, '"')) {
            s = replaceAllExact(s, "\"", "\"\"");
            needEscape = true;
        } else {
            needEscape = contains(s, ',') || contains(s, '\n');
        }

        return needEscape ? '"' + s + '"' : s;
    }

    /**
     * Splits a comma-separated value line
     * 
     * @param s the line
     * @return the tokens
     **/
    public final static String[] splitCSV(final String s) {
        return splitCSVdelim(s, ',');
    }

    public final static String[] splitCSVdelim(final String s, final char delim) {
        int i = 0;
        final int len = s.length();
        final List<String> tokens = new ArrayList<String>();

        while (i < len) {
            final char c = s.charAt(i);
            if (c == delim) {
                i++;
                tokens.add("");
                continue;
            } else if (c == '"') {
                i++;
                int j = i;
                boolean needDoubleEscape = false, needBackslashEscape = false;
                while (true) {
                    j = s.indexOf('"', j);
                    if (j < 0) {
                        j = len;
                        break;
                    } else if (j == len - 1) {
                        break;
                    } else if ((j > 0) && (s.charAt(j - 1) == '\\')) {
                        needBackslashEscape = true;
                        j++;
                    } else if (s.charAt(j + 1) == '"') {
                        needDoubleEscape = true;
                        j += 2;
                    } else {
                        break;
                    }
                }
                String token = s.substring(i, j);
                if (needDoubleEscape) {
                    token = replaceAllExact(token, "\"\"", "\"");
                }
                if (needBackslashEscape) {
                    token = replaceAllExact(token, "\\\"", "\"");
                }
                if ((j + 1 < len) && (s.charAt(j + 1) != delim)) {
                    i = j + 1;
                    j = s.indexOf(delim, i);
                    if (j < 0) {
                        j = len;
                    }
                    token += s.substring(i, j);
                    i = j + 1;
                } else {
                    i = j + 2;
                }
                tokens.add(token);
            } else {
                int j = s.indexOf(delim, i);
                if (j < 0) {
                    j = len;
                }
                tokens.add(s.substring(i, j));
                i = j + 1;
            }
        }
        if (i == len) {
            tokens.add("");
        }

        return toStringArray(tokens);
    }

    public final static String[] splitRTF(String s) {
        if (s.endsWith("\\par")) {
            s = s.substring(0, s.length() - 4);
        }
        return splitExact(s, "\\tab ");
    }

    /**
     * Splits the source String into an array of substrings separated by a delimiter String; this
     * treats the delimiter String as the exact delimiter instead of a regular expression, making it
     * faster than String.split; should return the same results as src.split(delim) when delim has
     * no special characters, so trailing empty strings are removed
     * 
     * @param src the source String
     * @param delim the delimiter String
     * @return the array of substrings
     **/
    public final static String[] splitExact(final String src, final String delim) {
        return splitExact(src, delim, 0);
    }

    /**
     * Splits the source String into an array of substrings separated by a delimiter String; this
     * treats the delimiter String as the exact delimiter instead of a regular expression, making it
     * faster than String.split; should return the same results as src.split(delim, limit) when
     * delim has no special characters
     * 
     * @param src the source String
     * @param delim the delimiter String
     * @param limit the maximum number of Strings to return
     * @return the array of substrings
     **/
    public final static String[] splitExact(final String src, final String delim, final int limit) {
        return toStringArray(splitExactIntoList(src, delim, limit));
    }

    /**
     * Splits the source String into an List of substrings separated by a delimiter String;
     * this treats the delimiter String as the exact delimiter instead of a regular expression,
     * making it faster than String.split; should return results equivalent to src.split(delim) when
     * delim has no special characters, so trailing empty strings are removed
     * 
     * @param src the source String
     * @param delim the delimiter String
     * @return the List of substrings
     **/
    public final static List<String> splitExactIntoList(final String src, final String delim) {
        return splitExactIntoList(src, delim, 0);
    }

    /**
     * Splits the source String into an List of substrings separated by a delimiter String;
     * this treats the delimiter String as the exact delimiter instead of a regular expression,
     * making it faster than String.split; should return results equivalent to src.split(delim,
     * limit) when delim has no special characters
     * 
     * @param src the source String
     * @param delim the delimiter String
     * @param limit the maximum number of Strings to return
     * @return the List of substrings
     **/
    public final static List<String> splitExactIntoList(final String src, final String delim, final int limit) {
        final int len = src.length(), dlen = delim.length();
        int stop = -dlen, i = 0;
        final int limitm1 = limit - 1;

        if ((dlen == 0) || (len == 0)) {
            return arrayToArrayList(src.split(delim, limit));
        }
        final List<String> list = new ArrayList<String>();
        while (stop < len) {
            stop += dlen;
            final int start = stop;
            if (i == limitm1) {
                list.add(src.substring(start));
                return list;
            }
            stop = src.indexOf(delim, start);
            if (stop < 0) {
                stop = len;
            }
            list.add(src.substring(start, stop));
            i++;
        }
        if (limit == 0) {
            for (i = list.size() - 1; i >= 0; i--) {
                if (list.get(i).equals("")) {
                    list.remove(i);
                } else {
                    break;
                }
            }
        }

        return list;
    }

    /**
     * Splits the source String into an array of substrings separated by a delimiter char; this
     * treats the delimiter char as the exact delimiter instead of a regular expression, making it
     * faster than String.split; should return the same results as src.split(delim) when delim has
     * no special characters, so trailing empty strings are removed
     * 
     * @param src the source String
     * @param delim the delimiter char
     * @return the array of substrings
     **/
    public final static String[] splitExact(final String src, final char delim) {
        return splitExact(src, delim, 0);
    }

    /**
     * Splits the source String into an array of substrings separated by a delimiter char; this
     * treats the delimiter char as the exact delimiter instead of a regular expression, making it
     * faster than String.split; should return the same results as src.split(delim, limit) when
     * delim has no special characters
     * 
     * @param src the source String
     * @param delim the delimiter char
     * @param limit the maximum number of Strings to return
     * @return the array of substrings
     **/
    public final static String[] splitExact(final String src, final char delim, final int limit) {
        return toStringArray(splitExactIntoList(src, delim, limit));
    }

    /**
     * Splits the source String into an List of substrings separated by a delimiter char; this
     * treats the delimiter char as the exact delimiter instead of a regular expression, making it
     * faster than String.split; should return results equivalent to src.split(delim) when delim has
     * no special characters, so trailing empty strings are removed
     * 
     * @param src the source String
     * @param delim the delimiter char
     * @return the List of substrings
     **/
    public final static List<String> splitExactIntoList(final String src, final char delim) {
        return splitExactIntoList(src, delim, 0);
    }

    /**
     * Splits the source String into an List of substrings separated by a delimiter char; this
     * treats the delimiter char as the exact delimiter instead of a regular expression, making it
     * faster than String.split; should return results equivalent to src.split(delim, limit) when
     * delim has no special characters
     * 
     * @param src the source String
     * @param delim the delimiter char
     * @param limit the maximum number of Strings to return
     * @return the List of substrings
     **/
    public final static List<String> splitExactIntoList(final String src, final char delim, final int limit) {
        if (src == null) {
            return null;
        }
        final int len = src.length();
        int stop = -1, i = 0;
        final int limitm1 = limit - 1;

        if (len == 0) {
            return arrayToArrayList(src.split(Character.toString(delim), limit));
        }
        final List<String> list = new ArrayList<String>();
        while (stop < len) {
            stop += 1;
            final int start = stop;
            if (i == limitm1) {
                list.add(src.substring(start));
                return list;
            }
            stop = src.indexOf(delim, start);
            if (stop < 0) {
                stop = len;
            }
            list.add(src.substring(start, stop));
            i++;
        }
        if (limit == 0) {
            for (i = list.size() - 1; i >= 0; i--) {
                if (list.get(i).equals("")) {
                    list.remove(i);
                } else {
                    break;
                }
            }
        }

        return list;
    }

    /**
     * Removes all instances of the given character from the given String
     * 
     * @param s the String
     * @param toRemove the character to remove
     * @return the String
     **/
    public final static String remove(final String s, final char toRemove) {
        final int len = length(s);

        if (len == 0) {
            return null;
        }

        StringBuilder sb = null;
        char[] chars = null;
        int start = 0;
        for (int i = 0; i < len; i++) {
            final char c = s.charAt(i);
            if (c == toRemove) {
                if (chars == null) {
                    sb = new StringBuilder(len);
                    chars = s.toCharArray();
                }
                sb.append(chars, start, i - start);
                start = i + 1;
            }
        }

        if (sb == null) {
            return s;
        } else if (start < len) {
            sb.append(chars, start, len - start);
        }

        return sb.toString();
    }

    /**
     * Replaces occurrences of a source String with a new substring in the original String; this
     * treats the original substring as the exact value instead of a regular expression, making it
     * faster than String.replaceAll; should return the same result as src.replaceAll(o, n) when o
     * has no special characters
     * 
     * @param src the source String
     * @param o the original substring
     * @param n the new substring
     * @return the String after replacement
     **/
    public final static String replaceAllExact(final String src, final String o, final String n) {
        if (src == null) {
            return null;
        }

        int i = src.indexOf(o, 0);
        if (i == -1) {
            return src;
        }

        int start = 0;
        final int olen = o.length();
        final StringBuilder sb = new StringBuilder();

        do {
            sb.append(src.substring(start, i));
            sb.append(n);
            start = i + olen;
        } while ((i = src.indexOf(o, start)) != -1);
        sb.append(src.substring(start));

        return sb.toString();
    }

    /**
     * Replaces occurrences of a source String with a new substring in the original file
     * 
     * @param file the source file
     * @param o the original substring
     * @param n the new substring
     * @throws Exception if an I/O problem occurs
     **/
    public final static void replaceAllExactFile(final String file, final String o, final String n)
            throws Exception {
        writeFile(file, replaceAllExact(readFile(file), o, n));
    }

    /**
     * Replaces occurrencesof a source String with a new substring in the files in the path
     * 
     * @param path the source path
     * @param o the original substring
     * @param n the new substring
     * @throws Exception if an I/O problem occurs
     **/
    public final static void replaceAllExactPath(String path, final String o, final String n) throws Exception {
        final File f = new File(path);

        if (!f.exists()) {
            throw new Exception("Invalid path:  " + path);
        } else if (f.isFile()) {
            replaceAllExactFile(path, o, n);
        } else if (f.isDirectory()) {
            path = formatDir(path);
            final String[] list = f.list();
            final int len = length(list);
            for (int i = 0; i < len; i++) {
                replaceAllExactPath(path + list[i], o, n);
            }
        }
    }

    /**
     * Converts an array into an ArrayList
     * 
     * @param a the array
     * @param <E> the array type
     * @return the ArrayList
     **/
    public final static <E> ArrayList<E> arrayToArrayList(final E[] a) {
        final ArrayList<E> list = new ArrayList<E>(a.length);

        for (final E e : a) {
            list.add(e);
        }

        return list;
    }

    /**
     * Clears a StringBuffer so that it can be reused without allocating another StringBuffer
     * 
     * @param sb the StringBuffer
     **/
    public final static void clear(final StringBuffer sb) {
        if (sb != null) {
            sb.setLength(0); //sb.delete(0, sb.length()); is slower
        }
    }

    /**
     * Clears a StringBuilder so that it can be reused without allocating another StringBuilder
     * 
     * @param sb the StringBuilder
     **/
    public final static void clear(final StringBuilder sb) {
        if (sb != null) {
            sb.setLength(0);
        }
    }

    /**
     * Clears a Collection
     * 
     * @param c the Collection
     **/
    public final static void clear(final Collection<?> c) {
        if (c != null) {
            c.clear();
        }
    }

    /**
     * Clears a Map
     * 
     * @param m the Map
     **/
    public final static void clear(final Map<?, ?> m) {
        if (m != null) {
            m.clear();
        }
    }

    /**
     * Clears an Object[] array
     * 
     * @param a the Object[] array
     **/
    public final static void clear(final Object[] a) {
        for (int i = length(a); i >= 0; i--) {
            a[i] = null;
        }
    }

    public final static boolean remove(final Collection<?> c, final Object elem) {
        return (c != null) && c.remove(elem);
    }

    /**
     * Removes the given key from the given Map (if the Map is not null)
     * 
     * @param map the Map
     * @param key the key
     * @return the removed value
     */
    public final static <V> V remove(final Map<?, V> map, final Object key) {
        return map == null ? null : map.remove(key);
    }

    /**
     * Provides an easy way to write from an input stream to an output stream with a buffer
     * 
     * @param input InputStream content to be written
     * @param output OutputStream place to write content
     * @param bufferSize int size of data buffer
     * @param closeOutput boolean whether the OutputStream should be closed
     * @return the number of bytes that were copied
     * @throws IOException if an I/O problem occurs while reading or writing
     */
    public static int bufferedReadWrite(final InputStream input, final OutputStream output, final int bufferSize,
            final boolean closeOutput) throws IOException {
        int bytesRead, totalRead = 0;
        final byte[] buff = new byte[bufferSize];
        //BufferedInputStream inputStream = new BufferedInputStream(input);
        //BufferedOutputStream outputStream = new BufferedOutputStream(output);
        //from http://java.sun.com/j2se/1.4.2/docs/api/java/io/BufferedOutputStream.html:
        //an application can write bytes to the underlying output stream without necessarily causing a call to the underlying system for each byte written.
        // That doesn't help us since we're already reading/writing large chunks defined by the bufferSize parameter.

        try {
            while (-1 != (bytesRead = input.read(buff, 0, bufferSize))) {
                output.write(buff, 0, bytesRead);
                totalRead += bytesRead;
            }

            output.flush(); // Used to flush only if we didn't close it, but should flush then too, because some streams don't automatically flush on close.
            return totalRead;
        } finally {
            input.close(); // Don't need to check for null; if it was, we would've already gotten a NullPointerException.
            if (closeOutput) {
                output.close();
            }
        }
    }

    private static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));

    /**
     * Reads a line from standard input
     * 
     * @return the line
     **/
    public static final String readLine() {
        try {
            return in.readLine();
        } catch (final IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Prompts the user for information and reads it
     * 
     * @param prompt the prompt
     * @return the information
     **/
    public static final String promptReadLine(final String prompt) {
        System.out.println(prompt); // User prompts written to System.out, not log
        return readLine();
    }

    /**
     * Prompts the user for information and reads it
     * 
     * @param prompt the prompt
     * @return the information
     **/
    public static final String promptRead(final String prompt) {
        System.out.print(prompt); // User prompts written to System.out, not log
        return readLine();
    }

    /**
     * Retrieves a String composed of n copies of s
     * 
     * @param s the String to replicate
     * @param n the desired number of copies of s
     * @return String
     **/
    public static final String replicate(final String s, final int n) {
        if (s == null) {
            return null;
        }

        final StringBuilder sb = new StringBuilder(n * s.length());

        for (int i = 0; i < n; i++) {
            sb.append(s);
        }

        return sb.toString();
    }

    /**
     * Retrieves the first n characters of s, or all of s if its length is less than n
     * 
     * @param s the String to truncate
     * @param n the desired number of characters
     * @return the truncated String
     **/
    public static final String truncate(final String s, final int n) {
        return length(s) <= n ? s : s.substring(0, n);
    }

    /**
     * Retrieves an InputStream that can be marked and marks it
     * 
     * @param in the InputStream
     * @return the markable InputStream
     **/
    public static final InputStream getMarkableInputStream(InputStream in) {
        in = in.markSupported() ? in : new BufferedInputStream(in);
        in.mark(MAX_MARK_BUFFER);
        return in;
    }

    /**
     * Retrieves an InputStream that can be marked and marks it
     * 
     * @param in the InputStream
     * @param buffer the buffer size
     * @return the markable InputStream
     **/
    public static final InputStream getMarkableInputStream(InputStream in, final int buffer) {
        in = in.markSupported() ? in : new BufferedInputStream(in);
        in.mark(buffer);
        return in;
    }

    /**
     * Retrieves a Reader that can be marked and marks it
     * 
     * @param in the Reader
     * @return the markable Reader
     **/
    public static final Reader getMarkableReader(Reader in) {
        in = in.markSupported() ? in : new BufferedReader(in);
        try {
            in.mark(MAX_MARK_BUFFER);
        } catch (final IOException e) {
            throw new RuntimeException(e);
        }
        return in;
    }

    /**
     * Retrieves a BufferedInputStream for the given InputStream
     * 
     * @param in the InputStream
     * @return the BufferedInputStream
     **/
    public static final BufferedInputStream getBufferedInputStream(final InputStream in) {
        return in instanceof BufferedInputStream ? (BufferedInputStream) in : new BufferedInputStream(in);
    }

    /**
     * Retrieves a BufferedReader for the given Reader
     * 
     * @param in the Reader
     * @return the BufferedReader
     **/
    public static final BufferedReader getBufferedReader(final Reader in) {
        return in instanceof BufferedReader ? (BufferedReader) in : new BufferedReader(in);
    }

    /**
     * Retrieves a BufferedReader for the given location
     * 
     * @param location the location
     * @return the BufferedReader
     * @throws IOException if an I/O problem occurs
     **/
    public static final BufferedReader getBufferedReader(final String location) throws IOException {
        return getBufferedReader(getReader(location));
    }

    /**
     * Retrieves a BufferedOutputStream for the given OutputStream
     * 
     * @param out the OutputStream
     * @return the BufferedOutputStream
     **/
    public static final BufferedOutputStream getBufferedOutputStream(final OutputStream out) {
        return out instanceof BufferedOutputStream ? (BufferedOutputStream) out : new BufferedOutputStream(out);
    }

    /**
     * Retrieves a BufferedWriter for the given Writer
     * 
     * @param out the Writer
     * @return the BufferedWriter
     **/
    public static final BufferedWriter getBufferedWriter(final Writer out) {
        return out instanceof BufferedWriter ? (BufferedWriter) out : new BufferedWriter(out);
    }

    /**
     * Retrieves a PrintStream for the given OutputStream
     * 
     * @param out the OutputStream
     * @return the PrintStream
     **/
    public static final PrintStream getPrintStream(final OutputStream out) {
        return out instanceof PrintStream ? (PrintStream) out : new PrintStream(out);
    }

    public static final PrintStream getPrintStream(final String loc) {
        try {
            return getPrintStream(getFileOutputStream(loc));
        } catch (final IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Retrieves a PrintWriter for the given Writer
     * 
     * @param out the Writer
     * @return the PrintWriter
     **/
    public static final PrintWriter getPrintWriter(final Writer out) {
        return out instanceof PrintWriter ? (PrintWriter) out : new PrintWriter(out);
    }

    private static Map<Class<?>, String> classMap = new java.util.IdentityHashMap<Class<?>, String>();

    private static Map<Class<?>, String> shortClassMap = new java.util.IdentityHashMap<Class<?>, String>();

    /**
     * Looks up a Class name in a cache, adding it if it is not present
     * 
     * @param c the Class
     * @return the name String
     **/
    public static final String lookupClassName(final Class<?> c) {
        String name = classMap.get(c);

        if (name == null) {
            name = c.getName();
            int i = name.lastIndexOf('$') + 1;
            final int size = name.length();

            for (; i < size; i++) {
                if (Character.isJavaIdentifierStart(name.charAt(i))) {
                    break;
                }
            }

            if (i > 0) {
                name = name.substring(i);
            }
            classMap.put(c, name);
        }

        return name;
    }

    /**
     * Returns the full class name for the given Class
     * 
     * @param c the Class for which to find the name
     * @return the full class name
     **/
    public static final String getFullClassName(final Class<?> c) {
        if (c.isArray()) {
            return getFullClassName(c.getComponentType()) + "[]";
        }
        final Class<?> declaringClass = c.getDeclaringClass();
        if (declaringClass != null) {
            return getFullClassName(declaringClass) + '.' + lookupClassName(c);
        }

        return lookupClassName(c);
    }

    /**
     * Returns the full class name for the given Object
     * 
     * @param o the Object for which to find the name
     * @return the full class name
     **/
    public static final String getFullClassName(final Object o) {
        return o == null ? null : getFullClassName(o.getClass());
    }

    /**
     * Returns the class name for the given Class
     * 
     * @param c the Class for which to find the name
     * @return the class name
     **/
    public static final String getClassName(final Class<?> c) {
        if (c == null) {
            return null;
        }
        String name = shortClassMap.get(c);
        if (name == null) {
            name = getFullClassName(c);
            final int i = name.lastIndexOf('.');
            name = i < 0 ? name : name.substring(i + 1);
            shortClassMap.put(c, name);
        }

        return name;
    }

    /**
     * Strips undisplayable characters from the String
     * 
     * @param s the String
     * @return the stripped String
     **/
    public static final String strip(final String s) {
        return strip(s, false, HANDLE_UNKNOWN_INCLUDE);
    }

    public final static int HANDLE_UNKNOWN_EXCEPTION = 0;

    public final static int HANDLE_UNKNOWN_INCLUDE = 1;

    public final static int HANDLE_UNKNOWN_SKIP = 2;

    /**
     * Strips undisplayable characters from the String
     * 
     * @param s the String
     * @param clean whether known characters should be replaced with clean characters instead of
     *            stripped
     * @param handleUnknownMode when cleaning, this indicates that an Exception should be thrown for
     *            unknown characters
     * @return the stripped String
     **/
    public static final String strip(final String s, final boolean clean, final int handleUnknownMode) {
        if (s == null) {
            return null;
        }

        StringBuilder sb = null;
        char[] chars = null;
        int start = 0;
        final int len = s.length();
        for (int i = 0; i < len; i++) {
            final char c = s.charAt(i);
            if (!isDisplayable(c)) {
                if (sb == null) {
                    sb = new StringBuilder(len);
                    chars = s.toCharArray();
                }
                sb.append(chars, start, i - start);
                if (clean) {
                    switch (c) {
                    // Used to put actual characters in comments, but that caused problems when Ubuntu users checked out and recommitted code
                    case 130:
                    case 8218:
                        sb.append(',');
                        break;
                    case 136:
                    case 710:
                        sb.append('^');
                        break;
                    case 139:
                    case 8249:
                        sb.append('<');
                        break;
                    case 145:
                    case 8216:
                    case 146:
                    case 8217:
                    case 180:
                        sb.append('\'');
                        break;
                    case 147: // Left "
                    case 8220:
                    case 148: // Right "
                    case 8221:
                        sb.append('"');
                        break;
                    case 150:
                    case 8211:
                    case 151: // Long -
                    case 8212:
                        sb.append('-');
                        break;
                    case 155:
                    case 8250:
                        sb.append('>');
                        break;
                    case 169: // Copyright
                        sb.append("(C)");
                        break;
                    case 171:
                        sb.append("<<");
                        break;
                    case 178: // 2 Exponent
                        sb.append("^2");
                        break;
                    case 179: // 3 Exponent
                        sb.append("^3");
                        break;
                    case 187:
                        sb.append(">>");
                        break;
                    case 188: // Fraction
                        sb.append("1/4");
                        break;
                    case 189: // Fraction
                        sb.append("1/2");
                        break;
                    case 190: // Fraction
                        sb.append("3/4");
                        break;
                    case 215: // Multiplication
                        sb.append('*');
                        break;
                    case 247: // Division
                        sb.append('/');
                        break;
                    default:
                        switch (handleUnknownMode) {
                        case HANDLE_UNKNOWN_EXCEPTION:
                            throw new RuntimeException("Unrecognized character " + (int) c + ": " + c);
                        case HANDLE_UNKNOWN_INCLUDE:
                            sb.append(c);
                            break;
                        }
                    }
                }
                start = i + 1;
            }
        }

        if (sb == null) {
            return s;
        } else {
            if (start < len) {
                sb.append(chars, start, len - start);
            }
            return sb.toString();
        }
    }

    /**
     * Replaces invalid UTF-8 characters with valid characters
     * 
     * @param s the String
     * @return the cleaned String
     **/
    public static final String cleanUTF8(final String s) {
        return strip(s, true, HANDLE_UNKNOWN_EXCEPTION);
    }

    /**
     * Retrieves whether the character is displayable
     * 
     * @param c the character
     * @return whether the character is displayable
     **/
    public static final boolean isDisplayable(final char c) {
        return ((c > 31) && (c < 127)) || (c == 9) || (c == 10) || (c == 13);
    }

    /**
     * Returns the class name for the given Object
     * 
     * @param o the Object for which to find the name
     * @return the class name
     **/
    public static final String getClassName(final Object o) {
        return o == null ? null : getClassName(o.getClass());
    }

    /**
     * Retrieves whether two Objects are identical
     * 
     * @param o1 the first Object
     * @param o2 the second Object
     * @return whether the two Objects are identical
     **/
    public final static boolean identicalTo(final Object o1, final Object o2) {
        return diff(o1, o2) == null;
    }

    /**
     * Retrieves the difference between two Objects
     * 
     * @param o1 the first Object
     * @param o2 the second Object
     * @return the difference String
     **/
    public final static String diff(final Object o1, final Object o2) {
        StringBuilder b = null;

        if ((o1 == null) && (o2 == null)) {
            return null;
        } else if (o1 == null) {
            return "o1 is null";
        } else if (o2 == null) {
            return "o2 is null";
        } else if (!o1.getClass().equals(o2.getClass())) {
            return "Class mismatch: " + o1.getClass() + ", " + o2.getClass();
        }

        for (Class<?> c = o1.getClass(); c != null; c = c.getSuperclass()) {
            final Field[] fa = c.getDeclaredFields();
            for (int i = 0; i < fa.length; i++) {
                final Field f = fa[i];
                if (Modifier.isStatic(f.getModifiers())) {
                    continue;
                }
                f.setAccessible(true);
                try {
                    final Object f1 = f.get(o1), f2 = f.get(o2);
                    final boolean match;
                    if ((f1 instanceof String) && (f2 instanceof String)) {
                        match = equalsIgnoreCase((String) f1, (String) f2);
                    } else {
                        match = equals(f1, f2);
                    }
                    if (!match) {
                        b = append(b, "(").append(f.getName()).append(": ").append(f1).append(", ").append(f2)
                                .append(")");
                    }
                } catch (final IllegalAccessException e) {
                    throw new RuntimeException(e);
                }
            }
        }

        return toString(b);
    }

    /**
     * Returns whether or not a character is a vowel.
     * 
     * @param c the character to determine if it is a vowel
     * @return true if c is a vowel, false otherwise
     **/
    public final static boolean isVowel(final char c) {
        switch (c) {
        case 'a':
        case 'e':
        case 'i':
        case 'o':
        case 'u':
        case 'A':
        case 'E':
        case 'I':
        case 'O':
        case 'U':
            return true;
        }

        return false;
    }

    public final static boolean isLegalFileNameCharacter(final char c) {
        if (illegalFileNameCharacters == null) {
            final StringBuilder sb = new StringBuilder();
            final String tempDir = getTempDir();
            log.trace("Checking for legal file name characters, using temp directory " + tempDir);
            for (char t = 0; t < 256; t++) {
                final String tempName = tempDir + "good" + t + "good.txt";
                final File f = new File(tempName);
                try {
                    f.createNewFile();
                } catch (final Exception e) {
                    log.trace("Illegal file name character " + c + ", couldn't create " + tempName, e);
                    sb.append(t);
                }
                f.delete();
            }
            illegalFileNameCharacters = sb.toString();
            final int size = illegalFileNameCharacters.length();
            if (size == 0) {
                throw new RuntimeException("Found no illegal characters");
            } else if (size == 256) {
                throw new RuntimeException("Found no legal characters, couldn't create any temp files");
            }
        }
        return (c < 256) && !contains(illegalFileNameCharacters, c);
    }

    /**
     * Removes characters from the String that aren't valid for file names
     * 
     * @param s the String
     * @return the file name String
     **/
    public final static String toFileName(final String s) {
        final StringBuilder sb = new StringBuilder();
        for (int i = 0, len = s.length(); i < len; i++) {
            final char c = s.charAt(i);
            //if (Character.isLetter(c) || Character.isDigit(c) || c == '-' || c == '.' || c == '_')
            if (isLegalFileNameCharacter(c)) {
                sb.append(c);
            }
        }
        return sb.toString();
    }

    public final static List<File> getAllFiles(final File f) {
        final List<File> list = new ArrayList<File>();
        addAllFiles(list, f);
        return list;
    }

    private final static void addAllFiles(final List<File> list, final File f) {
        if (!f.isDirectory()) {
            list.add(f);
            return;
        }
        final File[] children = f.listFiles();
        final int size = length(children);
        if (size == 0) {
            return;
        }
        ensureCapacity(list, list.size() + size);
        for (final File child : children) {
            addAllFiles(list, child);
        }
    }

    public final static Class<?>[] getClasses() {
        return getClasses((String) null);
    }

    private final static class Resource {

        public final String containerPath;

        public final String resourcePath;

        public Resource(final String containerPath, final String resourcePath) {
            this.containerPath = containerPath;
            this.resourcePath = resourcePath;
        }
    }

    public final static class PackageCriterion implements Criterion {

        private final String prefix;

        private PackageCriterion(final String rootPackage) {
            this.prefix = rootPackage.endsWith(".") ? rootPackage : rootPackage + '.';
        }

        public final static PackageCriterion getInstance(final String rootPackage) {
            return rootPackage == null ? null : new PackageCriterion(rootPackage);
        }

        public final static PackageCriterion getInstance(final Class<?> siblingClass) {
            return siblingClass == null ? null : new PackageCriterion(siblingClass.getPackage().getName());
        }

        @Override
        public final boolean isMet(final Object o) {
            return ((String) o).startsWith(this.prefix);
        }
    }

    public final static Class<?>[] getClasses(final String rootPackage) {
        return getClasses(PackageCriterion.getInstance(rootPackage), null);
    }

    public final static Class<?>[] getClasses(final Criterion nameCriterion, final Criterion classCriterion) {
        return getClasses(nameCriterion, classCriterion, null);
    }

    private final static Class<?>[] getClasses(final Criterion nameCriterion, final Criterion classCriterion,
            final String ext) {
        final String[] _paths = getClassPath();
        List<String> paths = Arrays.asList(_paths);
        if (ext != null) {
            paths = new ArrayList<String>(paths);
            paths.add(ext);
        }
        final List<Resource> nameList = new ArrayList<Resource>();

        for (final String path : paths) {
            //log.info(path);
            final File f = new File(path);
            if (f.isDirectory()) {
                final int pathLength = f.getAbsolutePath().length() + 1;
                for (final File child : getAllFiles(f)) {
                    //log.info('\t' + child.getAbsolutePath().substring(pathLength));
                    nameList.add(new Resource(path, child.getAbsolutePath().substring(pathLength)));
                }
            } else if (path.endsWith(".jar")) {
                final JarFile jf;
                try {
                    jf = new JarFile(f);
                } catch (final IOException e) {
                    throw toRuntimeException(e);
                }
                final Enumeration<JarEntry> entries = jf.entries();
                while (entries.hasMoreElements()) {
                    nameList.add(new Resource(path, entries.nextElement().getName()));
                }
                try {
                    jf.close();
                } catch (final IOException e) {
                    // Can't close
                }
            }
        }

        final List<Class<?>> classList = new ArrayList<Class<?>>();
        final Set<String> nameSet = new HashSet<String>();
        for (final Resource resource : nameList) {
            final String childName = resource.resourcePath;
            if (childName.endsWith(".class")) {
                final String className = childName.substring(0, childName.length() - 6).replace('/', '.')
                        .replace('\\', '.');
                if (((nameCriterion == null) || nameCriterion.isMet(className)) && !nameSet.contains(className)) {
                    nameSet.add(className);
                    final Class<?> c;
                    try {
                        c = ReflectUtil.forName(className);
                        //} catch (final NoClassDefFoundError e)
                        //{   // Just keep looking
                        //} catch (final IncompatibleClassChangeError e)
                        //{   // Just keep looking
                    } catch (final Throwable e) {
                        throw new RuntimeException("Error loading class " + className + " from " + childName
                                + " in " + resource.containerPath, e);
                    }
                    if ((classCriterion == null) || classCriterion.isMet(c)) {
                        addIfNotContainedOrNull(classList, c);
                    }
                }
            }
        }

        return classList.toArray(EMPTY_ARRAY_CLASS);
    }

    public final static <T> Class<? extends T>[] getClasses(final Class<T> parent) {
        return getClasses(null, parent);
    }

    public final static <T> Class<? extends T>[] getClasses(final String rootPackage, final Class<T> parent) {
        return getClasses(rootPackage, null, parent, null);
    }

    public final static <T> Class<? extends T>[] getClasses(final String rootPackage, final Criterion nameCriterion,
            final Class<T> parent, final Criterion classCriterion) {
        final Criterion nc = Criteria.getCriterion(PackageCriterion.getInstance(rootPackage), nameCriterion);
        final Criterion cc = Criteria.getCriterion(AssignableFromCriterion.getInstance(parent), classCriterion);
        return cast(getClasses(nc, cc));
    }

    private final static String getLocation(final Class<?> c) {
        final String className = c.getName();
        final int dot = className.lastIndexOf('.') + 1;
        final String packageName = className.substring(0, dot),
                packagePath = replaceAllExact(packageName, ".", "/");
        final String classLoc = packagePath + className.substring(dot) + ".class";
        URL url = Util.class.getClassLoader().getResource(classLoc);
        if (url == null) {
            final ClassLoader classLoader = c.getClassLoader();
            url = classLoader.getResource(classLoc);
            ReflectUtil.registerClassLoader(classLoader);
        }
        final String p = replaceAllExact(url.toString(), "%20", " "); // Replace with generic unescaping
        if (p.startsWith("jar:")) {
            return p.substring(p.startsWith("jar:file:") ? 9 : 4, p.indexOf('!'));
        } else {
            return p.substring(p.startsWith("file:") ? 5 : 0, p.lastIndexOf('/') + 1);
        }
    }

    /**
     * Retrieves the Classes of a package
     * 
     * @param c a Class in the desired package
     * @return the Classes of the desired package
     **/
    public final static Class<?>[] getClassesForPackage(final Class<?> c) {
        return getClasses(PackageCriterion.getInstance(c), null, getLocation(c));
    }

    public final static Class<?>[] getClassesForPackage(final Class<?> c, final Criterion nameCriterion) {
        return getClasses(Criteria.getCriterion(PackageCriterion.getInstance(c), nameCriterion), null,
                getLocation(c));
    }

    /**
     * Writes information about a .jar
     * 
     * @param path the path of the .jar
     * @param w the Writer
     * @throws Exception if an I/O problem occurs
     **/
    public final static void jarInfo(final String path, final Writer w) throws Exception {
        final JarFile jar = new JarFile(new File(path));
        final Enumeration<JarEntry> en = jar.entries();
        final PrintWriter pw = getPrintWriter(w);

        while (en.hasMoreElements()) {
            final String name = en.nextElement().getName();
            if (!name.endsWith(".class")) {
                continue;
            }
            try {
                Class<?> c = Class.forName(name.substring(0, name.length() - 6).replace('/', '.'));
                final Member[][] allMembers = new Member[][] { c.getFields(), c.getConstructors(), c.getMethods() };
                pw.print(c);
                for (c = c.getSuperclass(); c != null; c = c.getSuperclass()) {
                    pw.print(" extends ");
                    pw.print(c.getName());
                }
                pw.println();
                pw.println('{');
                for (int j = 0; j < allMembers.length; j++) {
                    final Member[] members = allMembers[j];
                    for (final Member member : members) {
                        if (!Modifier.isPublic(member.getModifiers())) {
                            continue;
                        }
                        pw.print('\t');
                        pw.print(member);
                        pw.println(';');
                    }
                    if ((members.length > 0) && (j < allMembers.length - 1)) {
                        pw.println();
                    }
                }
                pw.println('}');
            } catch (final Throwable e) {
                pw.println(e);
            }
            pw.println();
        }

        pw.flush();
        jar.close();
    }

    /**
     * Throws an Error if needed
     * 
     * @param error whether there was an error
     * @param errMsg the error message
     **/
    public final static void err(final boolean error, final String errMsg) {
        if (error) {
            throw new Error(errMsg);
        }
    }

    /**
     * Opposite of String.intern; retrieves a String instance not used elsewhere in the JVM which
     * has the same value as the given String
     * 
     * @param s the String to copy
     * @return the unique copy of the String
     **/
    public final static String unique(final String s) {
        // First intern it, so that if someone else does stumble on this string value and interns it, they'll get that instance.
        // Then copy it, so no one else will get our copied instance.
        return new String(s.intern());
    }

    /**
     * Retrieves a globally unique identifier (GUID)
     * 
     * @return the GUID
     **/
    public final static String guid() {
        // http://www.darrenhobbs.com/archives/2003/08/java_has_guids.html
        // unique over time within a JVM
        //return new java.rmi.server.UID().toString();

        // uniqueness across ALL JVM's
        return new java.rmi.dgc.VMID().toString();
    }

    /**
     * Retrieves a universally unique identifier (UUID)
     * 
     * @return the UUID
     **/
    public final static String uuid() {
        // The above method, guid(), should be more reliable for ensuring uniqueness.
        // Java does not have access to the MAC address when creating a UUID.
        // The advantage of this method is that it conforms to the UUID format.
        return UUID.randomUUID().toString();
    }

    /**
     * Retrieves whether the given String is an OID
     * 
     * @param s the String
     * @return whether the String is an OID
     **/
    public final static boolean isOid(final String s) {
        int size = length(s);
        boolean hasDot = false, prevDot;

        if (size == 0) {
            return false;
        } else if (!isDigit(s.charAt(0))) {
            return false;
        }
        prevDot = false;

        size--;
        if (!isDigit(s.charAt(size))) {
            return false;
        }

        for (int i = 1; i < size; i++) {
            final char c = s.charAt(i);
            if (c == '.') {
                if (prevDot) {
                    return false;
                }
                hasDot = true;
                prevDot = true;
            } else if (!isDigit(c)) {
                return false;
            } else {
                prevDot = false;
            }
        }

        return hasDot;
    }

    public final static void assertOid(final String s) {
        if ((s != null) && !isOid(s)) {
            throw new RuntimeException("Expected an OID but found " + s);
        }
    }

    /**
     * Retrieves whether the given String is a UUID http://en.wikipedia.org/wiki/UUID a UUID
     * consists of 32 hexadecimal digits, displayed in 5 groups separated by hyphens, in the form
     * 8-4-4-4-12 for a total of 36 characters. For example: 550e8400-e29b-41d4-a716-446655440000
     * 
     * @param s the String
     * @return whether the String is a UUID
     **/
    public final static boolean isUuid(final String s) {
        if (length(s) != 36) {
            return false;
        }

        for (int i = 0; i < 36; i++) {
            final char c = s.charAt(i);
            if ((i == 8) || (i == 13) || (i == 18) || (i == 23)) {
                if (c != '-') {
                    return false;
                }
            } else {
                //if (!isLowercaseHex(c)) {
                // https://tools.regenstrief.org/jira/browse/CORE-952
                if (!isHex(c)) {
                    return false;
                }
            }
        }

        return true;
    }

    /**
     * Retrieves the name of the class that launched the current application
     * 
     * @return the name
     **/
    public final static String getApplication() {
        String name = getProperty(PROP_APPLICATION);
        if (name == null) {
            name = generateApplication();
            System.setProperty(PROP_APPLICATION, name);
        }

        return name;
    }

    /**
     * Retrieves the name of the class that launched the current application
     * 
     * @return the name
     **/
    public final static String generateApplication() {
        final StackTraceElement elem = generateApplicationElement();

        return elem == null ? null : elem.getClassName();
    }

    /**
     * Retrieves the StackTraceElement of the class that launched the current application
     * 
     * @return the StackTraceElement
     **/
    public final static StackTraceElement generateApplicationElement() {
        final StackTraceElement[] trace = new Exception().getStackTrace();
        for (int i = trace.length - 1; i >= 0; i--) {
            final StackTraceElement elem = trace[i];
            if (elem.getClassName().startsWith("org.regenstrief.")) {
                return elem;
            }
        }

        return null;
    }

    /**
     * Looks at the second command line argument for the name of an outputfile
     * 
     * @param args String[] command line arguments
     * @return String
     */
    public final static String getOutputFileName(final String[] args) {
        if (length(args) > 1) {
            final String arg = args[1].trim();
            if (arg.length() > 0) {
                log.info(" Output file: " + arg);
                return arg; // If get here, have a command line file to use for input
            }
        }
        return null;
    }

    /**
     * Retrieves the union array of the two given arrays
     * 
     * @param a1 the first Object[] array
     * @param a2 the second Object[] array
     * @return the union Object[] array
     **/
    public final static Object[] union(final Object[] a1, final Object... a2) {
        final Object[] ret = new Object[a1.length + a2.length];

        System.arraycopy(a1, 0, ret, 0, a1.length);
        System.arraycopy(a2, 0, ret, a1.length, a2.length);

        return ret;
    }

    /**
     * Retrieves the union array of the two given Collections
     * 
     * @param c1 the first Collection
     * @param c2 the second Collection
     * @param <E> the element type
     * @return the union Collection
     **/
    public final static <E> Collection<E> union(final Collection<E> c1, final Collection<E> c2) {
        final int size = size(c1) + size(c2);

        if (size == 0) {
            return null;
        }

        final List<E> ret = new ArrayList<E>(size);
        addAll(ret, c1);
        addAll(ret, c2);

        return ret;
    }

    /**
     * Concatenates two Strings
     * 
     * @param s1 the first String
     * @param s2 the second String
     * @return String
     **/
    public final static String concat(final String s1, final String s2) {
        return concatWithDelim(s1, s2, DEFAULT_CONCAT_DELIM);
    }

    /**
     * Concatenates two Strings with the given delimiter
     * 
     * @param s1 the first String
     * @param s2 the second String
     * @param delim the delimiter
     * @return String
     **/
    public final static String concatWithDelim(final String s1, final String s2, final String delim) {
        return s1 == null ? s2 : s2 == null ? s1 : s1 + delim + s2;
    }

    /**
     * Concatenates three Strings
     * 
     * @param s1 the first String
     * @param s2 the second String
     * @param s3 the third String
     * @return String
     **/
    public final static String concat(final String s1, final String s2, final String s3) {
        return concatWithDelim(s1, s2, s3, DEFAULT_CONCAT_DELIM);
    }

    /**
     * Concatenates three Strings with the given delimiter
     * 
     * @param s1 the first String
     * @param s2 the second String
     * @param s3 the third String
     * @param delim the delimiter
     * @return String
     **/
    public final static String concatWithDelim(final String s1, final String s2, final String s3,
            final String delim) {
        return concatWithDelim(concatWithDelim(s1, s2, delim), s3, delim);
    }

    /**
     * Retrieves n!
     * 
     * @param n n
     * @return n!
     **/
    public final static long factorial(final int n) {
        long f = 1;

        for (int i = 2; i <= n; i++) {
            f *= i;
        }

        return f;
    }

    /**
     * Parses the tag with the given tag name from the given XML
     * 
     * @param xml the XML String
     * @param tagName the tag name
     * @return the tag
     **/
    public final static String parseTag(final String xml, final String tagName) {
        if (xml == null) {
            return null;
        }

        final int start = startIndex(xml, tagName);
        if (start < 0) {
            return null;
        }
        final int end = endIndex(xml, tagName);

        if (end < start) {
            return null;
        }

        return xml.substring(start, end);
    }

    /**
     * Parses the value for the given tag name from the given XML
     * 
     * @param xml the XML String
     * @param tagName the tag name
     * @return the value
     **/
    public final static String parseValue(final String xml, final String tagName) {
        final String tag = parseTag(xml, tagName);
        if (tag == null) {
            return null;
        }

        final int start = tag.indexOf('>') + 1, end = tag.lastIndexOf('<');

        return start >= end ? null : tag.substring(start, end);
    }

    /**
     * Retrieves the start index of a tag
     * 
     * @param xml the String containing the tag
     * @param tagName the tag name
     * @return the start index
     **/
    public final static int startIndex(final String xml, final String tagName) {
        int index = 0;
        final int len = tagName.length();
        int i = 0;

        while (index >= 0) {
            index = xml.indexOf(tagName, i);
            if (index < 0) {
                return index;
            } else if (index > 0) {
                // Confirm this tagName we found is within <tagName>
                if ((xml.charAt(index - 1) == '<') && (xml.charAt(index + len) == '>')) {
                    return index - 1;
                }
            }
            i = index + 1;
        }

        return -1;
    }

    /**
     * Retrieves the end index of a tag
     * 
     * @param xml the String containing the tag
     * @param tagName the tag name
     * @return the end index
     **/
    public final static int endIndex(final String xml, final String tagName) {
        int index = 0;
        final int len = tagName.length();
        int i = xml.length();

        while (index >= 0) {
            index = xml.lastIndexOf(tagName, i);
            if (index < 0) {
                return index;
            } else if (index > 1) {
                if ((xml.charAt(index - 1) == '/') && (xml.charAt(index - 2) == '<')
                        && (xml.charAt(index + len) == '>')) {
                    return index + len + 1;
                }
            }
            i = index - 1;
        }

        return -1;
    }

    public final static boolean isIPAddress(final String ip) {
        final int ipSize = length(ip);
        if (ipSize == 0) {
            return false;
        }
        int fieldIndex = 0, fieldSize = 0;
        for (int i = 0; i < ipSize; i++) {
            final char c = ip.charAt(i);
            if (c == '.') {
                if (fieldSize == 0) {
                    return false;
                } else {
                    fieldIndex++;
                    fieldSize = 0;
                }
            } else if (isDigit(c)) {
                fieldSize++;
            } else {
                return false;
            }
        }
        return (fieldIndex == 3) && (fieldSize > 0);
    }

    /**
     * Retrieves a host's IP address; see SOAPClient.getLocalAddr() for local IP
     * 
     * @param hostName the host's name
     * @return the host's IP address
     **/
    public final static String getIPAddress(final String hostName) {
        try {
            //return new Socket(hostName, port).getInetAddress().getHostAddress();
            // See PropertyContext for dealing with ipv6 addresses
            return InetAddress.getByName(hostName).getHostAddress();
        } catch (final Exception e) {
            throw toRuntimeException(e);
        }
    }

    /**
     * Retrieves a host's name
     * 
     * @param ip the host's IP address
     * @return the host's name
     **/
    public final static String getHostName(final String ip) {
        //String[] tokens = splitExact(ip, '.');
        try {
            //return InetAddress.getByAddress(new byte[] {(byte) Integer.parseInt(tokens[0]), (byte) Integer.parseInt(tokens[1]), (byte) Integer.parseInt(tokens[2]), (byte) Integer.parseInt(tokens[3])}).getHostName();
            return InetAddress.getByName(ip).getHostName(); // The host name can either be a machine name, such as "java.sun.com", or a textual representation of its IP address
        } catch (final Exception e) {
            throw toRuntimeException(e);
        }
    }

    /**
     * Retrieves a new instance of the given class name
     * 
     * @param key the key of the property containing the desired class name
     * @param defaultClass the Class to instantiate if the given property is undefined
     * @return the instance Object
     **/
    public final static Object newInstance(final String key, final Class<?> defaultClass) {
        final String s = getProperty(key);

        return s == null ? ReflectUtil.newInstance(defaultClass) : ReflectUtil.newInstance(s);
    }

    /**
     * Retrieves the Thread's Runnable target
     * 
     * @param thread the Thread
     * @return the Runnable target
     **/
    public final static Runnable getTarget(final Thread thread) {
        try {
            if (threadTarget == null) {
                threadTarget = ReflectUtil.getField(Thread.class, "target");
            }

            final Runnable r = (Runnable) threadTarget.get(thread);

            return r != null ? r : thread;
        } catch (final Exception e) {
            throw toRuntimeException(e);
        }
    }

    /**
     * Retrieves the parent of the current Thread (not tested)
     * 
     * @return the parent Thread
     **/
    public final static Thread getParentThread() {
        return getParentThread(Thread.currentThread());
    }

    /**
     * Retrieves the parent of the given Thread (not tested)
     * 
     * @param t the child Thread
     * @return the parent Thread
     **/
    public final static Thread getParentThread(final Thread t) {
        final Thread[] g = new Thread[t.getThreadGroup().activeCount()];
        t.getThreadGroup().enumerate(g);

        return g[0];
    }

    /**
     * Displays and logs information
     * 
     * @param name the log file name
     * @param s the information String
     * @throws Exception if an I/O problem occurs
     **/
    public final static void log(final String name, final String s) throws Exception {
        appendFile(name, s + getLineSeparator());
        log.info(s);
    }

    /**
     * Retrieves the caller of the method that is invoking getCaller()
     * 
     * @return the caller
     **/
    public final static String getCaller() {
        final StackTraceElement stack[] = new Exception().getStackTrace();

        // stack[0] is this method; stack[1] called this method; stack[2] called stack[1] (stack[1] wants to know its own caller)
        if (length(stack) < 3) {
            return null;
        }

        return stack[2].toString();
    }

    /**
     * Retrieves the stack trace of the method that is invoking getStackTrace()
     * 
     * @return the stack trace
     **/
    public final static String getStackTrace() {
        final StackTraceElement stack[] = new Exception().getStackTrace();
        final int size = stack.length;
        final StringBuilder sb = new StringBuilder();
        final String sep = getLineSeparator();

        for (int i = 1; i < size; i++) {
            if (i > 1) {
                sb.append(sep);
            }
            sb.append(stack[i].toString());
        }

        return sb.toString();
    }

    /**
     * Converts the given Number to a Double
     * 
     * @param n the Number
     * @return the Double
     **/
    public final static Double toDouble(final Number n) {
        return n == null ? null : n instanceof Double ? (Double) n : new Double(n.doubleValue());
    }

    /**
     * Retrieves a Date relative to the current Date
     * 
     * @param numDays the offset number of days (positive to generate Dates after today, negative
     *            for Dates before today)
     * @return the Date
     **/
    public final static Date todayOffsetDate(final int numDays) {
        return new Date(todayOffsetTime(numDays));
    }

    public final static Date offsetDate(final Date date, final int numDays) {
        //return new Date(offsetTime(date.getTime(), numDays)); // Doesn't preserve the orignal Date's time zone
        final Calendar c = Dates.toCalendar(date);
        c.add(Calendar.DAY_OF_MONTH, numDays);
        return c.getTime();
    }

    public final static long todayOffsetTime(final int numDays) {
        return offsetTime(StopWatch.currentTime(), numDays);
    }

    public final static long offsetTime(final long time, final int numDays) {
        return time + (StopWatch.MILLIS_PER_DAY * numDays);
    }

    public final static boolean before(final Date date, final long compareTime) {
        return date.getTime() < compareTime;
    }

    public final static boolean after(final Date date, final long compareTime) {
        return date.getTime() > compareTime;
    }

    public final static boolean afterTodayOffset(final Date date, final int numDays) {
        //return date.after(todayOffsetDate(numDays));
        return after(date, todayOffsetTime(numDays));
    }

    public final static boolean beforeTodayOffset(final Date date, final int numDays) {
        //return date.before(todayOffsetDate(numDays));
        return before(date, todayOffsetTime(numDays));
    }

    public final static Date min(final Date a, final Date b) {
        if (a == null) {
            return b;
        } else if (b == null) {
            return a;
        }
        return a.before(b) ? a : b;
    }

    public final static Date max(final Date a, final Date b) {
        if (a == null) {
            return b;
        } else if (b == null) {
            return a;
        }
        return a.after(b) ? a : b;
    }

    /**
     * Retrieves whether the given Class is equal to or a subclass/implementation of the given class
     * name. Unlike className.equals(c.getName()), this will also check interfaces and super
     * classes. Unlike Class.isAssignableFrom(Class), this can be invoked even if the className is
     * not a loaded class.
     * 
     * @param c the Class
     * @param className the class name
     * @return whether objects of the Class are instances of the Class with the given name
     **/
    public final static boolean instanceOf(final Class<?> c, final String className) {
        if ((c == null) || (className == null)) {
            return false;
        } else if (className.equals(c.getName())) {
            return true;
        } else if (instanceOf(c.getSuperclass(), className)) {
            return true;
        }

        final Class<?>[] interfaces = c.getInterfaces();
        for (int i = 0, len = length(interfaces); i < len; i++) {
            if (instanceOf(interfaces[i], className)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Retrieves whether the given Object is an instance of the given class name. Unlike
     * className.equals(o.getClass().getName()), this will also check interfaces and super classes.
     * Unlike Object instanceof [class name], this can be invoked even if the className is not a
     * loaded class.
     * 
     * @param o the Object
     * @param className the class name
     * @return whether the Object is an instance of the Class with the given name
     **/
    public final static boolean instanceOf(final Object o, final String className) {
        if (o == null) {
            return false;
        }

        return instanceOf(o.getClass(), className);
    }

    /**
     * Performs an unchecked cast, so that we only need to deal with the warning in one place
     * 
     * @param o the Object to cast
     * @param <T> the type to which we want to cast the Object
     * @return the Object
     **/
    @SuppressWarnings("unchecked")
    public final static <T> T cast(final Object o) {
        return (T) o;
    }

    public final static <T> T assertValued(final T t) {
        if (t == null) {
            throw new NullPointerException();
        }
        return t;
    }

    /**
     * Retrieves the value of the base raised to the power of the exponent
     * 
     * @param b the base
     * @param e the exponent
     * @return the value
     **/
    public final static int pow(final int b, final int e) {
        if (e < 0) {
            throw new IllegalArgumentException("pow does not support negative exponent: " + e);
        }

        int v = 1;

        for (int i = 0; i < e; i++) {
            v *= b;
        }

        return v;
    }

    /**
     * Makes a deep clone of an object using serialization. The object, including all of its
     * components, must be serializable.
     * 
     * @param t the object to be cloned
     * @return a deep clone of the input object
     * @throws IOException if an I/O error occurs with the input stream or output stream
     * @throws ClassNotFoundException if the class of the input object cannot be found upon
     *             deserialization
     */
    @SuppressWarnings("unchecked")
    public static <T extends Serializable> T deepCopy(final T t) throws IOException, ClassNotFoundException {
        ObjectOutputStream objectOutputStream = null;
        ObjectInputStream objectInputStream = null;
        try {
            final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
            objectOutputStream.writeObject(t);
            objectOutputStream.flush();

            objectInputStream = new ObjectInputStream(
                    new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));

            return (T) objectInputStream.readObject();
        } finally {
            IoUtil.close(objectOutputStream);
            IoUtil.close(objectInputStream);
        }
    }

    public final static boolean getBit(final int v, final int i) {
        return ((v & (1 << i)) >> i) == 1;
    }

    public final static void finalize(final Object o) {
        if (o == null) {
            return;
        }
        ReflectUtil.invoke(o, "finalize");
    }

    public final static <T> T call(final Callable<T> c) {
        try {
            return c == null ? null : c.call();
        } catch (final Exception e) {
            throw toRuntimeException(e);
        }
    }
}