blob: 0dcfdb307ae027a469515518daa9c651f71f3504 [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.util;
import com.intellij.openapi.util.Comparing;
import com.intellij.util.text.CharArrayCharSequence;
import gnu.trove.Equality;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
/**
* Author: msk
*/
@SuppressWarnings("MethodOverridesStaticMethodOfSuperclass")
public class ArrayUtil extends ArrayUtilRt {
public static final short[] EMPTY_SHORT_ARRAY = ArrayUtilRt.EMPTY_SHORT_ARRAY;
public static final char[] EMPTY_CHAR_ARRAY = ArrayUtilRt.EMPTY_CHAR_ARRAY;
public static final byte[] EMPTY_BYTE_ARRAY = ArrayUtilRt.EMPTY_BYTE_ARRAY;
public static final int[] EMPTY_INT_ARRAY = ArrayUtilRt.EMPTY_INT_ARRAY;
public static final boolean[] EMPTY_BOOLEAN_ARRAY = ArrayUtilRt.EMPTY_BOOLEAN_ARRAY;
public static final Object[] EMPTY_OBJECT_ARRAY = ArrayUtilRt.EMPTY_OBJECT_ARRAY;
public static final String[] EMPTY_STRING_ARRAY = ArrayUtilRt.EMPTY_STRING_ARRAY;
public static final Class[] EMPTY_CLASS_ARRAY = ArrayUtilRt.EMPTY_CLASS_ARRAY;
public static final long[] EMPTY_LONG_ARRAY = ArrayUtilRt.EMPTY_LONG_ARRAY;
public static final Collection[] EMPTY_COLLECTION_ARRAY = ArrayUtilRt.EMPTY_COLLECTION_ARRAY;
public static final File[] EMPTY_FILE_ARRAY = ArrayUtilRt.EMPTY_FILE_ARRAY;
public static final Runnable[] EMPTY_RUNNABLE_ARRAY = ArrayUtilRt.EMPTY_RUNNABLE_ARRAY;
public static final CharSequence EMPTY_CHAR_SEQUENCE = new CharArrayCharSequence(EMPTY_CHAR_ARRAY);
public static final ArrayFactory<String> STRING_ARRAY_FACTORY = new ArrayFactory<String>() {
@NotNull
@Override
public String[] create(int count) {
return newStringArray(count);
}
};
public static final ArrayFactory<Object> OBJECT_ARRAY_FACTORY = new ArrayFactory<Object>() {
@NotNull
@Override
public Object[] create(int count) {
return newObjectArray(count);
}
};
private ArrayUtil() { }
@NotNull
@Contract(pure=true)
public static byte[] realloc(@NotNull byte[] array, final int newSize) {
if (newSize == 0) {
return EMPTY_BYTE_ARRAY;
}
final int oldSize = array.length;
if (oldSize == newSize) {
return array;
}
final byte[] result = new byte[newSize];
System.arraycopy(array, 0, result, 0, Math.min(oldSize, newSize));
return result;
}
@NotNull
@Contract(pure=true)
public static boolean[] realloc(@NotNull boolean[] array, final int newSize) {
if (newSize == 0) {
return EMPTY_BOOLEAN_ARRAY;
}
final int oldSize = array.length;
if (oldSize == newSize) {
return array;
}
boolean[] result = new boolean[newSize];
System.arraycopy(array, 0, result, 0, Math.min(oldSize, newSize));
return result;
}
@NotNull
@Contract(pure=true)
public static int[] realloc(@NotNull int[] array, final int newSize) {
if (newSize == 0) {
return EMPTY_INT_ARRAY;
}
final int oldSize = array.length;
if (oldSize == newSize) {
return array;
}
final int[] result = new int[newSize];
System.arraycopy(array, 0, result, 0, Math.min(oldSize, newSize));
return result;
}
@NotNull
@Contract(pure=true)
public static <T> T[] realloc(@NotNull T[] array, final int newSize, @NotNull ArrayFactory<T> factory) {
final int oldSize = array.length;
if (oldSize == newSize) {
return array;
}
T[] result = factory.create(newSize);
if (newSize == 0) {
return result;
}
System.arraycopy(array, 0, result, 0, Math.min(oldSize, newSize));
return result;
}
@NotNull
@Contract(pure=true)
public static int[] append(@NotNull int[] array, int value) {
array = realloc(array, array.length + 1);
array[array.length - 1] = value;
return array;
}
@NotNull
@Contract(pure=true)
public static int[] insert(@NotNull int[] array, int index, int value) {
int[] result = new int[array.length + 1];
System.arraycopy(array, 0, result, 0, index);
result[index] = value;
System.arraycopy(array, index, result, index+1, array.length - index);
return result;
}
@NotNull
@Contract(pure=true)
public static byte[] append(@NotNull byte[] array, byte value) {
array = realloc(array, array.length + 1);
array[array.length - 1] = value;
return array;
}
@NotNull
@Contract(pure=true)
public static boolean[] append(@NotNull boolean[] array, boolean value) {
array = realloc(array, array.length + 1);
array[array.length - 1] = value;
return array;
}
@NotNull
@Contract(pure=true)
public static char[] realloc(@NotNull char[] array, final int newSize) {
if (newSize == 0) {
return EMPTY_CHAR_ARRAY;
}
final int oldSize = array.length;
if (oldSize == newSize) {
return array;
}
final char[] result = new char[newSize];
System.arraycopy(array, 0, result, 0, Math.min(oldSize, newSize));
return result;
}
@NotNull
@Contract(pure=true)
public static <T> T[] toObjectArray(@NotNull Collection<T> collection, @NotNull Class<T> aClass) {
@SuppressWarnings("unchecked") T[] array = (T[])Array.newInstance(aClass, collection.size());
return collection.toArray(array);
}
@NotNull
@Contract(pure=true)
public static <T> T[] toObjectArray(@NotNull Class<T> aClass, @NotNull Object... source) {
@SuppressWarnings("unchecked") T[] array = (T[])Array.newInstance(aClass, source.length);
System.arraycopy(source, 0, array, 0, array.length);
return array;
}
@NotNull
@Contract(pure=true)
public static Object[] toObjectArray(@NotNull Collection<?> collection) {
if (collection.isEmpty()) return EMPTY_OBJECT_ARRAY;
//noinspection SSBasedInspection
return collection.toArray(new Object[collection.size()]);
}
@NotNull
@Contract(pure=true)
public static int[] toIntArray(@NotNull Collection<Integer> list) {
int[] ret = newIntArray(list.size());
int i = 0;
for (Integer e : list) {
ret[i++] = e.intValue();
}
return ret;
}
@NotNull
@Contract(pure=true)
public static <T> T[] mergeArrays(@NotNull T[] a1, @NotNull T[] a2) {
if (a1.length == 0) {
return a2;
}
if (a2.length == 0) {
return a1;
}
final Class<?> class1 = a1.getClass().getComponentType();
final Class<?> class2 = a2.getClass().getComponentType();
final Class<?> aClass = class1.isAssignableFrom(class2) ? class1 : class2;
@SuppressWarnings("unchecked") T[] result = (T[])Array.newInstance(aClass, a1.length + a2.length);
System.arraycopy(a1, 0, result, 0, a1.length);
System.arraycopy(a2, 0, result, a1.length, a2.length);
return result;
}
@NotNull
@Contract(pure=true)
public static <T> T[] mergeCollections(@NotNull Collection<? extends T> c1, @NotNull Collection<? extends T> c2, @NotNull ArrayFactory<T> factory) {
T[] res = factory.create(c1.size() + c2.size());
int i = 0;
for (T t : c1) {
res[i++] = t;
}
for (T t : c2) {
res[i++] = t;
}
return res;
}
@NotNull
@Contract(pure=true)
public static <T> T[] mergeArrays(@NotNull T[] a1, @NotNull T[] a2, @NotNull ArrayFactory<T> factory) {
if (a1.length == 0) {
return a2;
}
if (a2.length == 0) {
return a1;
}
T[] result = factory.create(a1.length + a2.length);
System.arraycopy(a1, 0, result, 0, a1.length);
System.arraycopy(a2, 0, result, a1.length, a2.length);
return result;
}
@NotNull
@Contract(pure=true)
public static String[] mergeArrays(@NotNull String[] a1, @NotNull String... a2) {
return mergeArrays(a1, a2, STRING_ARRAY_FACTORY);
}
@NotNull
@Contract(pure=true)
public static int[] mergeArrays(@NotNull int[] a1, @NotNull int[] a2) {
if (a1.length == 0) {
return a2;
}
if (a2.length == 0) {
return a1;
}
int[] result = new int[a1.length + a2.length];
System.arraycopy(a1, 0, result, 0, a1.length);
System.arraycopy(a2, 0, result, a1.length, a2.length);
return result;
}
@NotNull
@Contract(pure=true)
public static byte[] mergeArrays(@NotNull byte[] a1, @NotNull byte[] a2) {
if (a1.length == 0) {
return a2;
}
if (a2.length == 0) {
return a1;
}
byte[] result = new byte[a1.length + a2.length];
System.arraycopy(a1, 0, result, 0, a1.length);
System.arraycopy(a2, 0, result, a1.length, a2.length);
return result;
}
/**
* Allocates new array of size <code>array.length + collection.size()</code> and copies elements of <code>array</code> and
* <code>collection</code> to it.
*
* @param array source array
* @param collection source collection
* @param factory array factory used to create destination array of type <code>T</code>
* @return destination array
*/
@NotNull
@Contract(pure=true)
public static <T> T[] mergeArrayAndCollection(@NotNull T[] array,
@NotNull Collection<T> collection,
@NotNull final ArrayFactory<T> factory) {
if (collection.isEmpty()) {
return array;
}
final T[] array2;
try {
array2 = collection.toArray(factory.create(collection.size()));
}
catch (ArrayStoreException e) {
throw new RuntimeException("Bad elements in collection: " + collection, e);
}
if (array.length == 0) {
return array2;
}
final T[] result = factory.create(array.length + collection.size());
System.arraycopy(array, 0, result, 0, array.length);
System.arraycopy(array2, 0, result, array.length, array2.length);
return result;
}
/**
* Appends <code>element</code> to the <code>src</code> array. As you can
* imagine the appended element will be the last one in the returned result.
*
* @param src array to which the <code>element</code> should be appended.
* @param element object to be appended to the end of <code>src</code> array.
* @return new array
*/
@NotNull
@Contract(pure=true)
public static <T> T[] append(@NotNull final T[] src, @Nullable final T element) {
return append(src, element, (Class<T>)src.getClass().getComponentType());
}
@NotNull
@Contract(pure=true)
public static <T> T[] prepend(final T element, @NotNull final T[] array) {
return prepend(element, array, (Class<T>)array.getClass().getComponentType());
}
@NotNull
@Contract(pure=true)
public static <T> T[] prepend(T element, @NotNull T[] array, @NotNull Class<T> type) {
int length = array.length;
T[] result = (T[])Array.newInstance(type, length + 1);
System.arraycopy(array, 0, result, 1, length);
result[0] = element;
return result;
}
@NotNull
@Contract(pure=true)
public static byte[] prepend(byte element, @NotNull byte[] array) {
int length = array.length;
final byte[] result = new byte[length + 1];
result[0] = element;
System.arraycopy(array, 0, result, 1, length);
return result;
}
@Contract(pure=true)
public static <T> T[] append(@NotNull final T[] src, final T element, @NotNull ArrayFactory<T> factory) {
int length = src.length;
T[] result = factory.create(length + 1);
System.arraycopy(src, 0, result, 0, length);
result[length] = element;
return result;
}
@NotNull
@Contract(pure=true)
public static <T> T[] append(@NotNull T[] src, @Nullable final T element, @NotNull Class<T> componentType) {
int length = src.length;
T[] result = (T[])Array.newInstance(componentType, length + 1);
System.arraycopy(src, 0, result, 0, length);
result[length] = element;
return result;
}
/**
* Removes element with index <code>idx</code> from array <code>src</code>.
*
* @param src array.
* @param idx index of element to be removed.
* @return modified array.
*/
@NotNull
@Contract(pure=true)
public static <T> T[] remove(@NotNull final T[] src, int idx) {
int length = src.length;
if (idx < 0 || idx >= length) {
throw new IllegalArgumentException("invalid index: " + idx);
}
T[] result = (T[])Array.newInstance(src.getClass().getComponentType(), length - 1);
System.arraycopy(src, 0, result, 0, idx);
System.arraycopy(src, idx + 1, result, idx, length - idx - 1);
return result;
}
@NotNull
@Contract(pure=true)
public static <T> T[] remove(@NotNull final T[] src, int idx, @NotNull ArrayFactory<T> factory) {
int length = src.length;
if (idx < 0 || idx >= length) {
throw new IllegalArgumentException("invalid index: " + idx);
}
T[] result = factory.create(length - 1);
System.arraycopy(src, 0, result, 0, idx);
System.arraycopy(src, idx + 1, result, idx, length - idx - 1);
return result;
}
@NotNull
@Contract(pure=true)
public static <T> T[] remove(@NotNull final T[] src, T element) {
final int idx = find(src, element);
if (idx == -1) return src;
return remove(src, idx);
}
@NotNull
@Contract(pure=true)
public static <T> T[] remove(@NotNull final T[] src, T element, @NotNull ArrayFactory<T> factory) {
final int idx = find(src, element);
if (idx == -1) return src;
return remove(src, idx, factory);
}
@NotNull
@Contract(pure=true)
public static int[] remove(@NotNull final int[] src, int idx) {
int length = src.length;
if (idx < 0 || idx >= length) {
throw new IllegalArgumentException("invalid index: " + idx);
}
int[] result = newIntArray(src.length - 1);
System.arraycopy(src, 0, result, 0, idx);
System.arraycopy(src, idx + 1, result, idx, length - idx - 1);
return result;
}
@NotNull
@Contract(pure=true)
public static short[] remove(@NotNull final short[] src, int idx) {
int length = src.length;
if (idx < 0 || idx >= length) {
throw new IllegalArgumentException("invalid index: " + idx);
}
short[] result = src.length == 1 ? EMPTY_SHORT_ARRAY : new short[src.length - 1];
System.arraycopy(src, 0, result, 0, idx);
System.arraycopy(src, idx + 1, result, idx, length - idx - 1);
return result;
}
@Contract(pure=true)
public static <T> int lastIndexOf(@NotNull final T[] src, final T obj) {
for (int i = src.length - 1; i >= 0; i--) {
final T o = src[i];
if (o == null) {
if (obj == null) {
return i;
}
}
else {
if (o.equals(obj)) {
return i;
}
}
}
return -1;
}
@Contract(pure=true)
public static int find(@NotNull int[] src, int obj) {
return indexOf(src, obj);
}
@Contract(pure=true)
public static <T> int find(@NotNull final T[] src, final T obj) {
return ArrayUtilRt.find(src, obj);
}
@Contract(pure=true)
public static boolean startsWith(@NotNull byte[] array, @NotNull byte[] prefix) {
if (array == prefix) {
return true;
}
int length = prefix.length;
if (array.length < length) {
return false;
}
for (int i = 0; i < length; i++) {
if (array[i] != prefix[i]) {
return false;
}
}
return true;
}
@Contract(pure=true)
public static <E> boolean startsWith(@NotNull E[] array, @NotNull E[] subArray) {
if (array == subArray) {
return true;
}
int length = subArray.length;
if (array.length < length) {
return false;
}
for (int i = 0; i < length; i++) {
if (!Comparing.equal(array[i], subArray[i])) {
return false;
}
}
return true;
}
@Contract(pure=true)
public static boolean startsWith(@NotNull byte[] array, int start, @NotNull byte[] subArray) {
int length = subArray.length;
if (array.length - start < length) {
return false;
}
for (int i = 0; i < length; i++) {
if (array[start + i] != subArray[i]) {
return false;
}
}
return true;
}
@Contract(pure=true)
public static <T> boolean equals(@NotNull T[] a1, @NotNull T[] a2, @NotNull Equality<? super T> comparator) {
if (a1 == a2) {
return true;
}
int length = a2.length;
if (a1.length != length) {
return false;
}
for (int i = 0; i < length; i++) {
if (!comparator.equals(a1[i], a2[i])) {
return false;
}
}
return true;
}
@Contract(pure=true)
public static <T> boolean equals(@NotNull T[] a1, @NotNull T[] a2, @NotNull Comparator<? super T> comparator) {
if (a1 == a2) {
return true;
}
int length = a2.length;
if (a1.length != length) {
return false;
}
for (int i = 0; i < length; i++) {
if (comparator.compare(a1[i], a2[i]) != 0) {
return false;
}
}
return true;
}
@NotNull
@Contract(pure=true)
public static <T> T[] reverseArray(@NotNull T[] array) {
T[] newArray = array.clone();
for (int i = 0; i < array.length; i++) {
newArray[array.length - i - 1] = array[i];
}
return newArray;
}
@NotNull
@Contract(pure=true)
public static int[] reverseArray(@NotNull int[] array) {
int[] newArray = array.clone();
for (int i = 0; i < array.length; i++) {
newArray[array.length - i - 1] = array[i];
}
return newArray;
}
public static void reverse(@NotNull char[] array) {
for (int i = 0; i < array.length; i++) {
swap(array, array.length - i - 1, i);
}
}
@Contract(pure=true)
public static int lexicographicCompare(@NotNull String[] obj1, @NotNull String[] obj2) {
for (int i = 0; i < Math.max(obj1.length, obj2.length); i++) {
String o1 = i < obj1.length ? obj1[i] : null;
String o2 = i < obj2.length ? obj2[i] : null;
if (o1 == null) return -1;
if (o2 == null) return 1;
int res = o1.compareToIgnoreCase(o2);
if (res != 0) return res;
}
return 0;
}
//must be Comparables
@Contract(pure=true)
public static <T> int lexicographicCompare(@NotNull T[] obj1, @NotNull T[] obj2) {
for (int i = 0; i < Math.max(obj1.length, obj2.length); i++) {
T o1 = i < obj1.length ? obj1[i] : null;
T o2 = i < obj2.length ? obj2[i] : null;
if (o1 == null) return -1;
if (o2 == null) return 1;
int res = ((Comparable)o1).compareTo(o2);
if (res != 0) return res;
}
return 0;
}
public static <T> void swap(@NotNull T[] array, int i1, int i2) {
final T t = array[i1];
array[i1] = array[i2];
array[i2] = t;
}
public static void swap(@NotNull int[] array, int i1, int i2) {
final int t = array[i1];
array[i1] = array[i2];
array[i2] = t;
}
public static void swap(@NotNull boolean[] array, int i1, int i2) {
final boolean t = array[i1];
array[i1] = array[i2];
array[i2] = t;
}
public static void swap(@NotNull char[] array, int i1, int i2) {
final char t = array[i1];
array[i1] = array[i2];
array[i2] = t;
}
public static <T> void rotateLeft(@NotNull T[] array, int i1, int i2) {
final T t = array[i1];
System.arraycopy(array, i1 + 1, array, i1, i2 - i1);
array[i2] = t;
}
public static <T> void rotateRight(@NotNull T[] array, int i1, int i2) {
final T t = array[i2];
System.arraycopy(array, i1, array, i1 + 1, i2 - i1);
array[i1] = t;
}
@Contract(pure=true)
public static int indexOf(@NotNull Object[] objects, @Nullable Object object) {
return indexOf(objects, object, 0, objects.length);
}
@Contract(pure=true)
public static int indexOf(@NotNull Object[] objects, Object object, int start, int end) {
if (object == null) {
for (int i = start; i < end; i++) {
if (objects[i] == null) return i;
}
}
else {
for (int i = start; i < end; i++) {
if (object.equals(objects[i])) return i;
}
}
return -1;
}
@Contract(pure=true)
public static <T> int indexOf(@NotNull List<T> objects, T object, @NotNull Equality<T> comparator) {
for (int i = 0; i < objects.size(); i++) {
if (comparator.equals(objects.get(i), object)) return i;
}
return -1;
}
@Contract(pure=true)
public static <T> int indexOf(@NotNull List<T> objects, T object, @NotNull Comparator<T> comparator) {
for (int i = 0; i < objects.size(); i++) {
if (comparator.compare(objects.get(i), object) == 0) return i;
}
return -1;
}
@Contract(pure=true)
public static <T> int indexOf(@NotNull T[] objects, T object, @NotNull Equality<T> comparator) {
for (int i = 0; i < objects.length; i++) {
if (comparator.equals(objects[i], object)) return i;
}
return -1;
}
@Contract(pure=true)
public static int indexOf(@NotNull int[] ints, int value) {
for (int i = 0; i < ints.length; i++) {
if (ints[i] == value) return i;
}
return -1;
}
@Contract(pure=true)
public static int indexOf(@NotNull short[] ints, short value) {
for (int i = 0; i < ints.length; i++) {
if (ints[i] == value) return i;
}
return -1;
}
@Contract(pure=true)
public static boolean contains(@Nullable final Object o, @NotNull Object... objects) {
return indexOf(objects, o) >= 0;
}
@Contract(pure=true)
public static boolean contains(@Nullable final String s, @NotNull String... strings) {
if (s == null) {
for (String str : strings) {
if (str == null) return true;
}
}
else {
for (String str : strings) {
if (s.equals(str)) return true;
}
}
return false;
}
@NotNull
@Contract(pure=true)
public static int[] newIntArray(int count) {
return count == 0 ? EMPTY_INT_ARRAY : new int[count];
}
@NotNull
@Contract(pure=true)
public static long[] newLongArray(int count) {
return count == 0 ? EMPTY_LONG_ARRAY : new long[count];
}
@NotNull
@Contract(pure=true)
public static String[] newStringArray(int count) {
return count == 0 ? EMPTY_STRING_ARRAY : new String[count];
}
@NotNull
@Contract(pure=true)
public static Object[] newObjectArray(int count) {
return count == 0 ? EMPTY_OBJECT_ARRAY : new Object[count];
}
@NotNull
@Contract(pure=true)
public static <E> E[] ensureExactSize(int count, @NotNull E[] sample) {
if (count == sample.length) return sample;
@SuppressWarnings({"unchecked"}) final E[] array = (E[])Array.newInstance(sample.getClass().getComponentType(), count);
return array;
}
@Nullable
@Contract(pure=true)
public static <T> T getFirstElement(@NotNull T[] array) {
return array.length > 0 ? array[0] : null;
}
@Nullable
@Contract(pure=true)
public static <T> T getLastElement(@NotNull T[] array) {
return array.length > 0 ? array[array.length - 1] : null;
}
@NotNull
@Contract(pure=true)
public static String[] toStringArray(@Nullable Collection<String> collection) {
return ArrayUtilRt.toStringArray(collection);
}
public static <T> void copy(@NotNull final Collection<? extends T> src, @NotNull final T[] dst, final int dstOffset) {
int i = dstOffset;
for (T t : src) {
dst[i++] = t;
}
}
}