package org.rr.commons.utils; import static org.rr.commons.utils.BooleanUtils.not; import java.util.Arrays; import java.util.Collections; import java.util.List; public class ArrayUtils { private ArrayUtils() {} /** * Tests if a specified variable is an array. If the variable is an array, * it returns <code>true</code>, otherwise, it returns <code>false</code>. * * @param value * The value to test. * @return A Boolean value that indicates whether a specified variable is an * array. */ public static boolean isArray(final Object value) { if (value != null && (value instanceof Object[] || value.getClass().isArray()) ) { return true; } return false; } /** * Creates an array for the given value. * @param <T> * @param values * @return An empty array if values is null, the array if we have a list or an array given with values or, at last, the given * value in an array of the type of the given value. */ @SuppressWarnings("unchecked") public static <T>T[] toArray(final T values) { if(values==null) { return null; } else if(values instanceof List) { return (T[]) ((List<T>)values).toArray(); } else if(isArray(values)) { try { return (T[])values; } catch(Exception e) { T[] t = (T[]) java.lang.reflect.Array.newInstance(values.getClass().getComponentType(), ((T[])values).length); System.arraycopy(values, 0, t, 0, ((T[])values).length); return t; } } else { T[] t = (T[]) java.lang.reflect.Array.newInstance(values.getClass().getComponentType(), 1); t[0] = values; return t; } } /** * Fetches the value from the given array at the specified index. If too small * for the requested index, <code>null</code> will be returned. * <BR><BR> * Never throws any kind of <code>{@link Exception}</code>. * * @param values The array from which the value should be fetched. * @param index The index where the value is located the array. * @return The array value or <code>null</code>. */ public static <T>T get(final T[] values, int index) { if(index < 0 || values==null || values.length <= index) { return null; } else { return values[index]; } } /** * Creates a <code>String</code> that consists of a number of substrings * in an array. * * @param values * A n'th-dimensional array that contains the substrings to be * joined. * @param delimiter * The character(s) used to separate the substrings in the * returned <code>String</code>. If this parameter is null, * the <i>,</i> character will be used as delimiter char. * * @return The processed <code>String</code>. * @see #split(String, String, int, int) */ public static String join(final Object[] values, final String delimiter) { //use the default delimiter if the parameter delimiter is null. String delimiterValue = delimiter; if (delimiterValue == null) { delimiterValue = ","; } StringBuilder returnValue = new StringBuilder(); for (int i = 0; i < values.length; i++) { if (values[i] instanceof Object[]) { returnValue.append(join((Object[])values[i], delimiter)); } else { returnValue.append(values[i]); } if (i + 1 != values.length) { returnValue.append(delimiterValue); } } return returnValue.toString(); } /** * Sets a range of elements in the Array to zero. In practice, a new array with * a size of 0 are created and returned. * * @param values The array to be cleared. * @return a new, empty array instance with the same type as the given array. * * @see #resize(Object[], int) */ @SuppressWarnings("unchecked") public static <T>T[] clear(final T[] values) { //create the empty result array return (T[]) java.lang.reflect.Array.newInstance( values.getClass().getComponentType(), 0); } /** * Deletes the last element of an array. The returned array is * a new array instance which has been decreased by one. If the * array is <code>null</code> or has a length of 0, a new, empty * array instance will be returned. * * @param values The array which last entry should be removed. * @return A new array instance without the last entry. * * @see #resize(Object[], int) * @see #append(Object[], Object) */ public static <T>T[] detachLast(final T[] values) { //returns an empty array if there are no values to detach. if (values==null || values.length==0) { return clear(values); } T[] result = resize(values, values.length-1); return result; } /** * Deletes the first element of an array. The returned array is * a new array instance which has been decreased by one. If the * array is <code>null</code> or has a length of 0, a new, empty * array instance will be returned. * * @param values The array which first entry should be removed. * @return A new array instance without the first entry. * * @see #resize(Object[], int) * @see #append(Object[], Object) */ public static <T>T[] detachFirst(final T[] values) { //returns an empty array if there are no values to detach. if (values==null || values.length==0) { return clear(values); } T[] result = extract(values, 1, values.length); return result; } /** * Changes the size of an array to the specified new size. * Existing data records will be deleted if the <code>size</code> * is smaller than the specified array. If the size is larger than * the <code>values</code> array, the additional array fields contains * <code>null</code> values. * * @param values The array to be resized. If this parameter is <code>null</code> a new Object array with the * desired size will be created. * @param size The new array size. If a negative value is specified, the size will be * set to the <code>values</code> length. * * @return The resized array. */ @SuppressWarnings("unchecked") public static <T>T[] resize(final T[] values, int size) { if (values==null) { return (T[]) java.lang.reflect.Array.newInstance(Object.class, 0); } //is the size negative, just set it to the length of the given array. //An exact copy of the given array will be created. if (size < 0) { size = values.length; } //create the resized array T[] result = (T[]) java.lang.reflect.Array.newInstance( values.getClass().getComponentType(), size); //fetch the number of array elements to copy int copy = size > values.length ? values.length : size; System.arraycopy(values, 0, result, 0, copy); return result; } /** * Appends the <code>Object</code> specified with the <code>value</code> parameter to * the object array specified with the <code>values</code> parameter. The <code>values</code> * array don't be touched or modified in any case! A new by one increased array of the same data type as the * given <code>values</code> array will be created and the <code>value</code> are appended at * the end of the increased array. * <br><br> * Take sure that the data type of the value matches to the data type of the <code>values</code> array. * * @param values Array to be resized by one. * @param value The value to be appended at the end of the increased array. * @return The by one increased array with the <code>value</code> in the last field. * * @throws RuntimeException if the array given with the <code>values</code> parameter did not matches to the <code>value</code> parameter. * @throws NullPointerException if one of the specified parameters are <code>null</code>. * * @see #union(Object[], Object[]) * @see #resize(Object[], int) * @see #prepend(Object[], Object) */ public static <T>T[] append(final T[] values, final T value) { T[] result = resize(values, values.length+1 ); result[values.length] = value; return result; } /** * Prepends the <code>Object</code> specified with the <code>value</code> parameter to * the object array specified with the <code>values</code> parameter. The <code>values</code> * array don't be touched or modified in any case! A new by one increased array of the same data type as the * given <code>values</code> array will be created and the <code>value</code> are appended at * the end of the increased array. * <br><br> * Take sure that the data type of the value matches to the data type of the <code>values</code> array. * * @param values Array to be resized by one. * @param value The value to be appended at the beginning of the increased array. * @return The by one increased array with the <code>value</code> in the first field. * * @throws RuntimeException if the array given with the <code>values</code> parameter did not matches to the <code>value</code> parameter. * @throws NullPointerException if one of the specified parameters are <code>null</code>. * * @see #union(Object[], Object[]) * @see #resize(Object[], int) * @see #append(Object[], Object) */ @SuppressWarnings("unchecked") public static <T>T[] prepend(final T[] values, final T value) { T[] result = (T[]) java.lang.reflect.Array.newInstance(values.getClass().getComponentType(), values.length+1); System.arraycopy(values, 0, result, 1, values.length); result[0] = value; return result; } /** * Randomizes the order of the elements in the array specified * with the <code>values</code> array. A new instance of the array * will be created. The order of the given array won't be touched. * * @param values The array to be shuffled. * @return The shuffled array. */ @SuppressWarnings("unchecked") public static <T>T[] shuffle(final T[] values) { List<T> list = Arrays.asList(resize(values, values.length)); Collections.shuffle(list); T[] result = (T[])list.toArray((Object[]) java.lang.reflect.Array.newInstance( values.getClass().getComponentType(), list.size())); return result; } /** * Extracts a random entry from the array specified with the * <code>values</code> parameter. * * @param values The values array from where a random entry should be returned. * @return A random entry from the specified array. Returns <code>null</code> * if the given array is <code>null</code> or has a size of 0. */ public static <T>T randomValue(final T[] values) { if (values==null || values.length==0) { return null; } T[] shuffled = shuffle(values); return shuffled[0]; } /** * Removes an entry of an array from the location specified with the * <code>index</code> parameter. A new array instance will be created. The * given array won't be touched. If the specified index is to large, no entry * will be removed but a new array instance will be returned. * * @param values The array from which an entry should be removed. * @param index The location where the entry, which should be removed, resists. * The index starts with 0 for the first array entry. * @return The new array instance decreased by one, without the value to be removed * or a new array instance with all entries if the index is before or behind of one of the * existing array entries. */ @SuppressWarnings("unchecked") public static <T>T[] remove(final T[] values, int index) { if (index > values.length-1 || index < 0) { return resize(values, values.length); } T[] result = (T[]) java.lang.reflect.Array.newInstance( values.getClass().getComponentType(), values.length-1); int pos = 0; for (int i = 0; i < values.length; i++) { if (i!=index) { result[pos] = values[i]; pos++; } } return result; } /** * Stores the <code>Object</code> specified with the <code>value</code> parameter to * the object array specified with the <code>values</code> parameter at the specified <code>index</code>. * If the index is behind the arrays size, the Array will be automatically resized, so the index matches. * If another <code>Object</code> is already stored at the given <code>index</code>, it will be replaced * by this new <code>value</code>. If the <code>index</code> is negative, the <code>value</code> will be * stored at it's negative location. The whole array will be shifted by the negative <code>index</code>. * <BR><BR> * <b>Attention!</b> if the given array is large enough, it will not be copied. The value will be put into * the given array instance specified with the <code>values</code> parameter and returned. * <br><br> * Take sure that the data type of the value matches to the data type of the <code>values</code> array. * * @param values The array an value should be put into. * @param value The value to be appended at the specified index of the array. * @return The array with the <code>value</code> in the field with the specified index. * * @throws RuntimeException if the array given with the <code>values</code> parameter did not matches to the <code>value</code> parameter. * * @see #union(Object[], Object[]) * @see #resize(Object[], int) */ @SuppressWarnings("unchecked") public static <T>T[] put(final T[] values, final T value, int index) { T[] result = values; if (values==null) { //create an array with the type of the given value parameter. if(value==null) { result = (T[]) java.lang.reflect.Array.newInstance(Object.class, 0); } else { result = (T[]) java.lang.reflect.Array.newInstance(value.getClass(), index+1); } } else if (values.length-1 < index) { result = resize(values, index+1 ); } //is the index smaller than 0, append the value to the front of the array //and shift the array by the negative index value if (index < 0) { int newSize = values.length + (index * -1); //create the resized array result = (T[]) java.lang.reflect.Array.newInstance(values.getClass().getComponentType(), newSize); System.arraycopy(values, 0, result, result.length - values.length, values.length); result[0] = value; return result; } result[index] = value; return result; } /** * Extracts these part of the <code>values</code> array which is defined * between <code>start</code> and <code>end</code>. The first array entry * starts with 0. * <br><br> * If the start or end parameter ist out of range, the range will be resized * to a valid value. No <code>ArrayIndexOutOfRange</code> exception is thrown. * * @param values The array to be extracted. * @param start The start position. * @param end The end position. * @return A new array instance with the same type as the array specified * with the <code>values</code> parameter which contains all values between * <code>start</code> and <code>end</code>. */ @SuppressWarnings("unchecked") public static <T>T[] extract(final T[] values, int start, int end) { //test if the end is lower than the start value and switch them if (start > end) { int tmp = start; start = end; end = tmp; } //test if the end is behind the array. if (values.length < end) { end = values.length; } //test if the start is lower than 0 if (start < 0) { start = 0; } //determine the new size for the array int size = end - start; //create the resized array T[] result = (T[]) java.lang.reflect.Array.newInstance( values.getClass().getComponentType(), size); System.arraycopy(values, start, result, 0, size); return result; } /** * Creates a separated array. The separation will be defined with any n'th value. * <br><br> * For example an array like <code>new String[] {"1", "yes", "2", "no", "3", "abort"}</code> * which is specified with the <code>values</code> parameter and the <code>n</code> value is 2, the result looks like: * <code>new String[] {new String[] {1,2,3}, new String[] {"yes","no","abort"}}</code> * * @param values All parameters that should be arranged. * @param n The number of groups that should be build. * @return An Object array that contains all grouped arrays. */ @SuppressWarnings("unchecked") public static <T>T[][] divide(final T[] values, int n) { T[][] holder = (T[][]) java.lang.reflect.Array.newInstance(values.getClass().getComponentType(), new int[] {n, values.length/n}); int count = 0; for (int i=0; i < (values.length / n); i++) { for (int j=0; j < n; j++) { holder[j][i] = values[count++]; } } return holder; } /** * Reverses the order of the elements in a n'th-dimensional Array. The * original array won't be touched. * * @param values The array to be reversed. * * @return The reversed array. */ @SuppressWarnings("unchecked") public static <T>T[] reverse(final T[] values) { T[] result = (T[]) java.lang.reflect.Array.newInstance(values.getClass().getComponentType(), values.length); int index = 0; for (int i = result.length; i > 0; i--) { if (values[i-1] instanceof Object[]) { result[index++] = (T) reverse((T[])values[i-1]); } else { result[index++] = values[i-1]; } } return result; } /** * Prints all given values to the standard output. * @param values The values to be printed. */ public static void print(int[] values) { for (int i : values) { System.out.println(i); } } /** * Tell whether the given array is empty or not. * * @param values the array to test if it contains any entries. * @return <code>true</code> if the length of the given array is 0 or the given array is <code>null</code>, <code>false</code> otherwise. */ public static boolean isEmpty(String[] values) { return values == null || values.length == 0; } /** * Tell whether the given array is empty or not. * * @param values the array to test if it contains any entries. * @return <code>false</code> if the length of the given array is 0 or the given array is <code>null</code>, <code>true</code> otherwise. */ public static boolean isNotEmpty(String[] values) { return not(isEmpty(values)); } }