/*******************************************************************************
* Copyright (c) 2004, 2009 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Andrew Niefer (IBM Corporation) - initial API and implementation
* Markus Schorn (Wind River Systems)
* Andrew Ferguson (Symbian)
* Mike Kucera (IBM)
* Sergey Prigogin (Google)
*******************************************************************************/
package org.eclipse.cdt.core.parser.util;
import java.lang.reflect.Array;
/**
* @noextend This class is not intended to be subclassed by clients.
*/
public abstract class ArrayUtil {
private static final int DEFAULT_LENGTH = 2;
/**
* Assumes that array contains nulls at the end, only.
* Appends element after the last non-null element.
* If the array is null or not large enough, a larger one is allocated, using
* the given class object.
*/
static public Object[] append(Class<?> c, Object[] array, Object obj) {
if (obj == null)
return array;
if (array == null || array.length == 0) {
array = (Object[]) Array.newInstance(c, DEFAULT_LENGTH);
array[0] = obj;
return array;
}
int i= findFirstNull(array);
if (i >= 0) {
array[i]= obj;
return array;
}
Object[] temp = (Object[]) Array.newInstance(c, Math.max(array.length * 2, DEFAULT_LENGTH));
System.arraycopy(array, 0, temp, 0, array.length);
temp[array.length] = obj;
return temp;
}
/**
* Assumes that array contains nulls at the end, only.
* @returns index of first null, or -1
*/
private static int findFirstNull(Object[] array) {
boolean haveNull= false;
int left= 0;
int right= array.length - 1;
while (left <= right) {
int mid= (left + right) / 2;
if (array[mid] == null) {
haveNull= true;
right= mid - 1;
} else {
left= mid + 1;
}
}
return haveNull ? right + 1 : -1;
}
/**
* Assumes that array contains nulls at the end, only.
* Appends object using the current length of the array.
* @since 4.0
*/
static public Object[] append(Class<?> c, Object[] array, int currentLength, Object obj) {
if (obj == null)
return array;
if (array == null || array.length == 0) {
array = (Object[]) Array.newInstance(c, DEFAULT_LENGTH);
array[0] = obj;
return array;
}
if (currentLength < array.length) {
assert array[currentLength] == null;
assert currentLength == 0 || array[currentLength - 1] != null;
array[currentLength]= obj;
return array;
}
Object[] temp = (Object[]) Array.newInstance(c, array.length * 2);
System.arraycopy(array, 0, temp, 0, array.length);
temp[array.length] = obj;
return temp;
}
/**
* Assumes that array contains nulls at the end, only.
* Appends element after the last non-null element.
* If the array is not large enough, a larger one is allocated.
* Null <code>array</code> is supported for backward compatibility only and only when T is Object.
*/
@SuppressWarnings("unchecked")
static public <T> T[] append(T[] array, T obj) {
if (obj == null)
return array;
if (array == null || array.length == 0) {
Class<? extends Object> c = array != null ? array.getClass().getComponentType() : Object.class;
array = (T[]) Array.newInstance(c, DEFAULT_LENGTH);
array[0] = obj;
return array;
}
int i= findFirstNull(array);
if (i >= 0) {
array[i]= obj;
return array;
}
T[] temp = (T[]) Array.newInstance(array.getClass().getComponentType(),
Math.max(array.length * 2, DEFAULT_LENGTH));
System.arraycopy(array, 0, temp, 0, array.length);
temp[array.length] = obj;
return temp;
}
/**
* Type safe version of {@link #append(Class, Object[], int, Object)}
* @since 5.1
*/
@SuppressWarnings("unchecked")
static public <T> T[] appendAt(Class<T> c, T[] array, int currentLength, T obj) {
return (T[]) append(c, array, currentLength, obj);
}
/**
* Trims the given array and returns a new array with no null entries.
* Assumes that nulls can be found at the end, only.
* if array == null, a new array of length 0 is returned
* if forceNew == true, a new array will always be created.
* if forceNew == false, a new array will only be created if the original array
* contained null entries.
*
* @param c the type of the new array
* @param array the array to be trimmed
* @param forceNew
*/
static public Object[] trim(Class<?> c, Object[] array, boolean forceNew) {
if (array == null)
return (Object[]) Array.newInstance(c, 0);
int i = array.length;
if (i == 0 || array[i - 1] != null) {
if (!forceNew) {
return array;
}
} else {
i= findFirstNull(array);
assert i >= 0;
}
Object[] temp = (Object[]) Array.newInstance(c, i);
System.arraycopy(array, 0, temp, 0, i);
return temp;
}
public static Object[] trim(Class<?> c, Object[] array) {
return trim(c, array, false);
}
/**
* Trims the given array and returns a new array with no null entries.
* Assumes that nulls can be found at the end, only.
* if forceNew == true, a new array will always be created.
* if forceNew == false, a new array will only be created if the original array
* contained null entries.
*
* @param array the array to be trimmed
* @param forceNew
* @since 5.2
*/
@SuppressWarnings("unchecked")
static public <T> T[] trim(T[] array, boolean forceNew) {
int i = array.length;
if (i == 0 || array[i - 1] != null) {
if (!forceNew) {
return array;
}
} else {
i= findFirstNull(array);
assert i >= 0;
}
T[] temp = (T[]) Array.newInstance(array.getClass().getComponentType(), i);
System.arraycopy(array, 0, temp, 0, i);
return temp;
}
/**
* Trims the given array and returns a new array with no null entries.
* Assumes that nulls can be found at the end, only.
*
* @param array the array to be trimmed
* @since 5.2
*/
static public <T> T[] trim(T[] array) {
return trim(array, false);
}
/**
* Takes contents of the two arrays up to the first <code>null</code> element and concatenates
* them.
* @param c The type of the element of the returned array if there was not enough free space
* in the destination array.
* @param dest The destination array. The elements of the source array are added to this array
* if there is enough free space in it. May be <code>null</code>.
* @param source The source array. May not be <code>null</code>.
* @return The concatenated array, which may be the same as the first parameter.
*/
public static Object[] addAll(Class<?> c, Object[] dest, Object[] source) {
if (source == null || source.length == 0)
return dest;
int numToAdd = findFirstNull(source);
if (numToAdd <= 0) {
if (numToAdd == 0) {
return dest;
}
numToAdd= source.length;
}
if (dest == null || dest.length == 0) {
dest = (Object[]) Array.newInstance(c, numToAdd);
System.arraycopy(source, 0, dest, 0, numToAdd);
return dest;
}
int firstFree = findFirstNull(dest);
if (firstFree < 0) {
firstFree= dest.length;
}
if (firstFree + numToAdd <= dest.length) {
System.arraycopy(source, 0, dest, firstFree, numToAdd);
return dest;
}
Object[] temp = (Object[]) Array.newInstance(c, firstFree + numToAdd);
System.arraycopy(dest, 0, temp, 0, firstFree);
System.arraycopy(source, 0, temp, firstFree, numToAdd);
return temp;
}
/**
* Takes contents of the two arrays up to the first <code>null</code> element and concatenates
* them.
* @param dest The destination array. The elements of the source array are added to this array
* if there is enough free space in it. May be <code>null</code>.
* @param source The source array. May not be <code>null</code>.
* @return The concatenated array, which may be the same as the first parameter.
* @since 5.2
*/
@SuppressWarnings("unchecked")
public static <T> T[] addAll(T[] dest, T[] source) {
if (source == null || source.length == 0)
return dest;
int numToAdd = findFirstNull(source);
if (numToAdd <= 0) {
if (numToAdd == 0) {
return dest;
}
numToAdd= source.length;
}
if (dest == null || dest.length == 0) {
Class<? extends Object> c = dest != null ?
dest.getClass().getComponentType() : source.getClass().getComponentType();
dest = (T[]) Array.newInstance(c, numToAdd);
System.arraycopy(source, 0, dest, 0, numToAdd);
return dest;
}
int firstFree = findFirstNull(dest);
if (firstFree < 0) {
firstFree= dest.length;
}
if (firstFree + numToAdd <= dest.length) {
System.arraycopy(source, 0, dest, firstFree, numToAdd);
return dest;
}
T[] temp = (T[]) Array.newInstance(dest.getClass().getComponentType(), firstFree + numToAdd);
System.arraycopy(dest, 0, temp, 0, firstFree);
System.arraycopy(source, 0, temp, firstFree, numToAdd);
return temp;
}
/**
* Returns whether the specified array contains the specified object. Comparison is by
* object identity.
* @param array the array to search
* @param obj the object to search for
* @return true if the specified array contains the specified object, or the specified array is null
*/
public static boolean contains(Object[] array, Object obj) {
return indexOf(array, obj) >= 0;
}
/**
* Returns the index into the specified array of the specified object, or -1 if the array does not
* contain the object, or if the array is null. Comparison is by object identity.
* @param array the array to search
* @param obj the object to search for
* @return the index into the specified array of the specified object, or -1 if the array does not
* contain the object, or if the array is null
*/
public static int indexOf(Object[] array, Object obj) {
int result = -1;
if (array != null) {
for (int i = 0; i < array.length; i++) {
if (array[i] == obj)
return i;
}
}
return result;
}
/**
* Assumes that array contains nulls at the end, only.
* Returns whether the specified array contains the specified object. Comparison is by
* object identity.
* @param array the array to search
* @param obj the object to search for
* @return true if the specified array contains the specified object, or the specified array is null
*/
public static boolean containsEqual(Object[] array, Object obj) {
return indexOfEqual(array, obj) != -1;
}
/**
* Assumes that array contains nulls at the end, only.
* Returns the index into the specified array of the specified object, or -1 if the array does not
* contain the object, or if the array is null. Comparison is by equals().
* @param comments the array to search
* @param comment the object to search for
* @return the index into the specified array of the specified object, or -1 if the array does not
* contain an equal object, or if the array is null
*/
public static int indexOfEqual(Object[] comments, Object comment) {
int result = -1;
if (comments != null) {
for (int i= 0; (i < comments.length) && (comments[i] != null); i++) {
if (comments[i].equals(comment))
return i;
}
}
return result;
}
/**
* Note that this should only be used when the placement of nulls within the array
* is unknown (due to performance efficiency).
*
* Removes all of the nulls from the array and returns a new array that contains all
* of the non-null elements.
*
* If there are no nulls in the original array then the original array is returned.
*/
public static Object[] removeNulls(Class<?> c, Object[] array) {
if (array == null)
return (Object[]) Array.newInstance(c, 0);
int i;
int validEntries = 0;
for (i = 0; i < array.length; i++) {
if (array[i] != null)
validEntries++;
}
if (array.length == validEntries)
return array;
Object[] newArray = (Object[]) Array.newInstance(c, validEntries);
int j = 0;
for (i = 0; i < array.length; i++) {
if (array[i] != null)
newArray[j++] = array[i];
}
return newArray;
}
/**
* Note that this should only be used when the placement of nulls within the array
* is unknown (due to performance efficiency).
*
* Removes all of the nulls from the array and returns a new array that contains all
* of the non-null elements.
*
* If there are no nulls in the original array then the original array is returned.
* @since 5.2
*/
@SuppressWarnings("unchecked")
public static <T> T[] removeNulls(T[] array) {
int i;
int validEntries = 0;
for (i = 0; i < array.length; i++) {
if (array[i] != null)
validEntries++;
}
if (array.length == validEntries)
return array;
T[] newArray = (T[]) Array.newInstance(array.getClass().getComponentType(), validEntries);
int j = 0;
for (i = 0; i < array.length; i++) {
if (array[i] != null)
newArray[j++] = array[i];
}
return newArray;
}
/**
* To improve performance, this method should be used instead of ArrayUtil#removeNulls(Class, Object[]) when
* all of the non-null elements in the array are grouped together at the beginning of the array
* and all of the nulls are at the end of the array.
* The position of the last non-null element in the array must also be known.
*/
public static Object[] removeNullsAfter(Class<?> c, Object[] array, int index) {
final int newLen= index + 1;
if (array != null && array.length == newLen)
return array;
Object[] newArray = (Object[]) Array.newInstance(c, newLen);
if (array != null && newLen > 0)
System.arraycopy(array, 0, newArray, 0, newLen);
return newArray;
}
/**
* Type safe version of {@link #removeNullsAfter(Class, Object[], int)}
* @since 5.1
*/
@SuppressWarnings("unchecked")
public static <T> T[] trimAt(Class<T> c, T[] array, int index) {
return (T[]) removeNullsAfter(c, array, index);
}
/**
* Inserts the obj at the beginning of the array, shifting the whole thing one index
* Assumes that array contains nulls at the end, only.
*/
public static Object[] prepend(Class<?> c, Object[] array, Object obj) {
if (obj == null)
return array;
if (array == null || array.length == 0) {
array = (Object[]) Array.newInstance(c, DEFAULT_LENGTH);
array[0] = obj;
return array;
}
int i = findFirstNull(array);
if (i >= 0) {
System.arraycopy(array, 0, array, 1, i);
array[0] = obj;
} else {
Object[] temp = (Object[]) Array.newInstance(c, array.length * 2);
System.arraycopy(array, 0, temp, 1, array.length);
temp[0] = obj;
array = temp;
}
return array;
}
/**
* Inserts the obj at the beginning of the array, shifting the whole thing one index
* Assumes that array contains nulls at the end, only.
* array must not be <code>null</code>.
* @since 5.2
*/
public static <T> T[] prepend(T[] array, T obj) {
assert array != null;
if (obj == null)
return array;
if (array.length == 0) {
array = newArray(array, DEFAULT_LENGTH);
array[0] = obj;
return array;
}
int i = findFirstNull(array);
if (i >= 0) {
System.arraycopy(array, 0, array, 1, i);
array[0] = obj;
} else {
T[] temp = newArray(array, array.length*2);
System.arraycopy(array, 0, temp, 1, array.length);
temp[0] = obj;
array = temp;
}
return array;
}
@SuppressWarnings("unchecked")
private static <T> T[] newArray(T[] array, int newLen) {
return (T[]) Array.newInstance(array.getClass().getComponentType(), newLen);
}
/**
* Removes first occurrence of element in array and moves objects behind up front.
* @since 4.0
*/
public static void remove(Object[] array, Object element) {
if (array != null) {
for (int i = 0; i < array.length; i++) {
if (element == array[i]) {
System.arraycopy(array, i + 1, array, i, array.length - i - 1);
array[array.length - 1]= null;
return;
}
}
}
}
static public int[] setInt(int[] array, int idx, int val) {
if (array == null) {
array = new int[DEFAULT_LENGTH > idx + 1 ? DEFAULT_LENGTH : idx + 1];
array[idx] = val;
return array;
}
if (array.length <= idx) {
int newLen = array.length * 2;
while (newLen <= idx)
newLen *= 2;
int[] temp = new int[newLen];
System.arraycopy(array, 0, temp, 0, array.length);
array = temp;
}
array[idx] = val;
return array;
}
/**
* Stores the specified array contents in a new array of specified
* runtime type.
* @param target the runtime type of the new array
* @param source the source array
* @return the current array stored in a new array with the
* specified runtime type, or null if source is null.
*/
@SuppressWarnings("unchecked")
public static <S, T> T[] convert(Class<T> target, S[] source) {
T[] result= null;
if (source != null) {
result= (T[]) Array.newInstance(target, source.length);
for (int i= 0; i < source.length; i++) {
result[i]= (T) source[i];
}
}
return result;
}
/**
* Returns a new array that contains all of the elements of the
* given array except the first one.
*
* @since 5.1
* @throws NullPointerException if args is null
* @throws IllegalArgumentException if args.length <= 0
*/
@SuppressWarnings("unchecked")
public static <T> T[] removeFirst(T[] args) {
int n = args.length;
if (n <= 0)
throw new IllegalArgumentException();
T[] newArgs = (T[]) Array.newInstance(args.getClass().getComponentType(), n - 1);
for (int i = 1; i < n; i++) {
newArgs[i - 1] = args[i];
}
return newArgs;
}
}