// *************************************************************************************************************************** | |
// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * | |
// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * | |
// * to you 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 org.apache.juneau.internal; | |
import static org.apache.juneau.internal.StringUtils.*; | |
import static org.apache.juneau.internal.ThrowableUtils.*; | |
import java.lang.reflect.*; | |
import java.util.*; | |
import org.apache.juneau.reflect.*; | |
/** | |
* Quick and dirty utilities for working with arrays. | |
*/ | |
public final class ArrayUtils { | |
/** | |
* Appends one or more elements to an array. | |
* | |
* @param <T> The element type. | |
* @param array The array to append to. | |
* @param newElements The new elements to append to the array. | |
* @return A new array with the specified elements appended. | |
*/ | |
@SuppressWarnings("unchecked") | |
public static <T> T[] append(T[] array, T...newElements) { | |
if (array == null) | |
return newElements; | |
if (newElements.length == 0) | |
return array; | |
T[] a = (T[])Array.newInstance(array.getClass().getComponentType(), array.length + newElements.length); | |
for (int i = 0; i < array.length; i++) | |
a[i] = array[i]; | |
for (int i = 0; i < newElements.length; i++) | |
a[i+array.length] = newElements[i]; | |
return a; | |
} | |
/** | |
* Appends one or more elements to an array. | |
* | |
* @param <T> The element type. | |
* @param array The array to append to. | |
* @param newElements The new elements to append to the array. | |
* @return A new array with the specified elements appended. | |
*/ | |
@SuppressWarnings("unchecked") | |
public static <T> T[] append(T[] array, Collection<T> newElements) { | |
assertFieldNotNull(array, "array"); | |
if (newElements.size() == 0) | |
return array; | |
T[] a = (T[])Array.newInstance(array.getClass().getComponentType(), array.length + newElements.size()); | |
for (int i = 0; i < array.length; i++) | |
a[i] = array[i]; | |
int l = array.length; | |
for (T t : newElements) | |
a[l++] = t; | |
return a; | |
} | |
/** | |
* Combine an arbitrary number of arrays into a single array. | |
* | |
* @param arrays Collection of arrays to combine. | |
* @return A new combined array, or <jk>null</jk> if all arrays are <jk>null</jk>. | |
*/ | |
@SuppressWarnings("unchecked") | |
public static <T> T[] combine(T[]...arrays) { | |
assertFieldNotNull(arrays, "arrays"); | |
int l = 0; | |
T[] a1 = null; | |
for (T[] a : arrays) { | |
if (a1 == null && a != null) | |
a1 = a; | |
l += (a == null ? 0 : a.length); | |
} | |
if (a1 == null) | |
return null; | |
T[] a = (T[])Array.newInstance(a1.getClass().getComponentType(), l); | |
int i = 0; | |
for (T[] aa : arrays) | |
if (aa != null) | |
for (T t : aa) | |
a[i++] = t; | |
return a; | |
} | |
/** | |
* Creates a new array with reversed entries. | |
* | |
* @param <T> The class type of the array. | |
* @param array The array to reverse. | |
* @return A new array with reversed entries, or <jk>null</jk> if the array was <jk>null</jk>. | |
*/ | |
@SuppressWarnings("unchecked") | |
public static <T> T[] reverse(T[] array) { | |
if (array == null) | |
return null; | |
Class<T> c = (Class<T>)array.getClass().getComponentType(); | |
T[] a2 = (T[])Array.newInstance(c, array.length); | |
for (int i = 0; i < array.length; i++) | |
a2[a2.length-i-1] = array[i]; | |
return a2; | |
} | |
/** | |
* Sorts the elements in an array without creating a new array. | |
* | |
* @param array The array to sort. | |
* @return The same array. | |
*/ | |
public static <T> T[] reverseInline(T[] array) { | |
if (array == null) | |
return null; | |
T t; | |
for (int i = 0, j = array.length-1; i < j; i++, j--) { | |
t = array[i]; | |
array[i] = array[j]; | |
array[j] = t; | |
} | |
return array; | |
} | |
/** | |
* Converts the specified array to a <c>Set</c>. | |
* | |
* <p> | |
* The order of the entries in the set are the same as the array. | |
* | |
* @param <T> The entry type of the array. | |
* @param array The array being wrapped in a <c>Set</c> interface. | |
* @return The new set. | |
*/ | |
public static <T> Set<T> asSet(final T[] array) { | |
assertFieldNotNull(array, "array"); | |
return new AbstractSet<T>() { | |
@Override /* Set */ | |
public Iterator<T> iterator() { | |
return new Iterator<T>() { | |
int i = 0; | |
@Override /* Iterator */ | |
public boolean hasNext() { | |
return i < array.length; | |
} | |
@Override /* Iterator */ | |
public T next() { | |
if (i >= array.length) | |
throw new NoSuchElementException(); | |
T t = array[i]; | |
i++; | |
return t; | |
} | |
@Override /* Iterator */ | |
public void remove() { | |
throw new UnsupportedOperationException(); | |
} | |
}; | |
} | |
@Override /* Set */ | |
public int size() { | |
return array.length; | |
} | |
}; | |
} | |
/** | |
* Returns an iterator against an array. | |
* | |
* <p> | |
* This works with any array type (e.g. <c>String[]</c>, <c>Object[]</c>, | |
* <code><jk>int</jk>[]</code>, etc...). | |
* | |
* @param array The array to create an iterator over. | |
* @return An iterator over the specified array. | |
*/ | |
public static Iterator<Object> iterator(final Object array) { | |
return new Iterator<Object>() { | |
int i = 0; | |
int length = array == null ? 0 : Array.getLength(array); | |
@Override /* Iterator */ | |
public boolean hasNext() { | |
return i < length; | |
} | |
@Override /* Iterator */ | |
public Object next() { | |
if (i >= length) | |
throw new NoSuchElementException(); | |
return Array.get(array, i++); | |
} | |
@Override /* Iterator */ | |
public void remove() { | |
throw new UnsupportedOperationException(); | |
} | |
}; | |
} | |
/** | |
* Converts the specified collection to an array. | |
* | |
* <p> | |
* Works on both object and primitive arrays. | |
* | |
* @param c The collection to convert to an array. | |
* @param componentType The component type of the collection. | |
* @return A new array. | |
*/ | |
public static <T> Object toArray(Collection<?> c, Class<T> componentType) { | |
Object a = Array.newInstance(componentType, c.size()); | |
Iterator<?> it = c.iterator(); | |
int i = 0; | |
while (it.hasNext()) | |
Array.set(a, i++, it.next()); | |
return a; | |
} | |
/** | |
* Returns <jk>true</jk> if the specified object is an array. | |
* | |
* @param array The array to test. | |
* @return <jk>true</jk> if the specified object is an array. | |
*/ | |
public static boolean isArray(Object array) { | |
return array != null && array.getClass().isArray(); | |
} | |
/** | |
* Converts the specified array to an <c>ArrayList</c> | |
* | |
* @param array The array to convert. | |
* @param componentType | |
* The type of objects in the array. | |
* It must match the actual component type in the array. | |
* @return A new {@link ArrayList} | |
*/ | |
@SuppressWarnings("unchecked") | |
public static <T> List<T> toList(Object array, Class<T> componentType) { | |
List<T> l = new ArrayList<>(Array.getLength(array)); | |
for (int i = 0; i < Array.getLength(array); i++) | |
l.add((T)Array.get(array, i)); | |
return l; | |
} | |
/** | |
* Shortcut for calling <c>myList.toArray(new T[myList.size()]);</c> | |
* | |
* @param c The collection being converted to an array. | |
* @param componentType The component type of the array. | |
* @return The collection converted to an array. | |
*/ | |
@SuppressWarnings("unchecked") | |
public static <T> T[] toObjectArray(Collection<?> c, Class<T> componentType) { | |
Object a = Array.newInstance(componentType, c.size()); | |
Iterator<?> it = c.iterator(); | |
int i = 0; | |
while (it.hasNext()) | |
Array.set(a, i++, it.next()); | |
return (T[])a; | |
} | |
/** | |
* Copies the specified array into the specified list. | |
* | |
* <p> | |
* Works on both object and primitive arrays. | |
* | |
* @param array The array to copy into a list. | |
* @param list The list to copy the values into. | |
* @return The same list passed in. | |
*/ | |
@SuppressWarnings({"unchecked","rawtypes"}) | |
public static List copyToList(Object array, List list) { | |
if (array != null) { | |
int length = Array.getLength(array); | |
for (int i = 0; i < length; i++) | |
list.add(Array.get(array, i)); | |
} | |
return list; | |
} | |
/** | |
* Returns <jk>true</jk> if the specified array contains the specified element using the {@link Object#equals(Object)} | |
* method. | |
* | |
* @param element The element to check for. | |
* @param array The array to check. | |
* @return | |
* <jk>true</jk> if the specified array contains the specified element, | |
* <jk>false</jk> if the array or element is <jk>null</jk>. | |
*/ | |
public static <T> boolean contains(T element, T[] array) { | |
return indexOf(element, array) != -1; | |
} | |
/** | |
* Returns <jk>true</jk> if the specified array contains the specified integer | |
* | |
* @param element The element to check for. | |
* @param array The array to check. | |
* @return | |
* <jk>true</jk> if the specified array contains the specified element, | |
* <jk>false</jk> if the array or element is <jk>null</jk>. | |
*/ | |
public static boolean contains(int element, int[] array) { | |
if (array != null) | |
for (int i : array) | |
if (element == i) | |
return true; | |
return false; | |
} | |
/** | |
* Returns the index position of the element in the specified array using the {@link Object#equals(Object)} method. | |
* | |
* @param element The element to check for. | |
* @param array The array to check. | |
* @return | |
* The index position of the element in the specified array, or <c>-1</c> if the array doesn't contain the | |
* element, or the array or element is <jk>null</jk>. | |
*/ | |
public static <T> int indexOf(T element, T[] array) { | |
if (element == null) | |
return -1; | |
if (array == null) | |
return -1; | |
for (int i = 0; i < array.length; i++) | |
if (element.equals(array[i])) | |
return i; | |
return -1; | |
} | |
/** | |
* Returns <jk>true</jk> if the specified array contains the specified element using the {@link String#equals(Object)} | |
* method. | |
* | |
* @param element The element to check for. | |
* @param array The array to check. | |
* @return | |
* <jk>true</jk> if the specified array contains the specified element, | |
* <jk>false</jk> if the array or element is <jk>null</jk>. | |
*/ | |
public static boolean contains(String element, String[] array) { | |
return indexOf(element, array) != -1; | |
} | |
/** | |
* Returns the index position of the element in the specified array using the {@link String#equals(Object)} method. | |
* | |
* @param element The element to check for. | |
* @param array The array to check. | |
* @return | |
* The index position of the element in the specified array, or | |
* <c>-1</c> if the array doesn't contain the element, or the array or element is <jk>null</jk>. | |
*/ | |
public static int indexOf(String element, String[] array) { | |
if (element == null) | |
return -1; | |
if (array == null) | |
return -1; | |
for (int i = 0; i < array.length; i++) | |
if (element.equals(array[i])) | |
return i; | |
return -1; | |
} | |
/** | |
* Converts a primitive wrapper array (e.g. <c>Integer[]</c>) to a primitive array (e.g. <code><jk>int</jk>[]</code>). | |
* | |
* @param o The array to convert. Must be a primitive wrapper array. | |
* @return A new array. | |
* @throws IllegalArgumentException If object is not a wrapper object array. | |
*/ | |
public static Object toPrimitiveArray(Object o) { | |
Class<?> c = o.getClass(); | |
if (! c.isArray()) | |
throw new IllegalArgumentException("Cannot pass non-array objects to toPrimitiveArray()"); | |
int l = Array.getLength(o); | |
Class<?> tc = ClassInfo.of(c.getComponentType()).getPrimitiveForWrapper(); | |
if (tc == null) | |
throw new IllegalArgumentException("Array type is not a primitive wrapper array."); | |
Object a = Array.newInstance(tc, l); | |
for (int i = 0; i < l; i++) | |
Array.set(a, i, Array.get(o, i)); | |
return a; | |
} | |
/** | |
* Converts an Iterable to a list. | |
* | |
* @param i The iterable to convert. | |
* @return A new list of objects copied from the iterable. | |
*/ | |
public static List<?> toList(Iterable<?> i) { | |
List<Object> l = new ArrayList<>(); | |
Iterator<?> i2 = i.iterator(); | |
while (i2.hasNext()) | |
l.add(i2.next()); | |
return l; | |
} | |
/** | |
* Returns the first object in the specified collection or array. | |
* | |
* @param val The collection or array object. | |
* @return | |
* The first object, or <jk>null</jk> if the collection or array is empty or <jk>null</jk> or the value | |
* isn't a collection or array. | |
*/ | |
public static Object getFirst(Object val) { | |
if (val != null) { | |
if (val instanceof Collection) { | |
Collection<?> c = (Collection<?>)val; | |
if (c.isEmpty()) | |
return null; | |
return c.iterator().next(); | |
} | |
if (val.getClass().isArray()) | |
return Array.getLength(val) == 0 ? null : Array.get(val, 0); | |
} | |
return null; | |
} | |
/** | |
* Converts the specified collection to an array of strings. | |
* | |
* <p> | |
* Entries are converted to strings using {@link #toString()}. | |
* <jk>null</jk> values remain <jk>null</jk>. | |
* | |
* @param c The collection to convert. | |
* @return The collection as a string array. | |
*/ | |
public static String[] toStringArray(Collection<?> c) { | |
String[] r = new String[c.size()]; | |
int i = 0; | |
for (Object o : c) | |
r[i++] = stringify(o); | |
return r; | |
} | |
/** | |
* Returns <jk>true</jk> if the following sorted arrays are equals. | |
* | |
* @param a1 Array #1. | |
* @param a2 Array #2. | |
* @return <jk>true</jk> if the following sorted arrays are equals. | |
*/ | |
public static boolean equals(String[] a1, String[] a2) { | |
if (a1.length != a2.length) | |
return false; | |
for (int i = 0; i < a1.length; i++) | |
if (! StringUtils.isEquals(a1[i], a2[i])) | |
return false; | |
return true; | |
} | |
/** | |
* Converts a collection to an array containing the elements in reversed order. | |
* | |
* @param c The component type of the array. | |
* @param l | |
* The collection to convert. | |
* <br>The collection is not modified. | |
* @return | |
* A new array, or <jk>null</jk> if the collection was <jk>null</jk>. | |
*/ | |
@SuppressWarnings("unchecked") | |
public static <T> T[] toReverseArray(Class<T> c, Collection<T> l) { | |
if (l == null) | |
return null; | |
Object a = Array.newInstance(c, l.size()); | |
Iterator<T> i = l.iterator(); | |
int j = l.size(); | |
while (i.hasNext()) | |
Array.set(a, --j, i.next()); | |
return (T[])a; | |
} | |
/** | |
* Removes the specified element from the specified array. | |
* | |
* @param element The element to remove from the array. | |
* @param array The array to remove the element from. | |
* @return A new array with the element removed, or the original array if the array did not contain the element. | |
*/ | |
public static Object[] remove(Object element, Object[] array) { | |
if (! contains(element, array)) | |
return array; | |
List<Object> l = new ArrayList<>(array.length); | |
for (Object o2 : array) { | |
if (! element.equals(o2)) | |
l.add(o2); | |
} | |
return l.toArray(new Object[l.size()]); | |
} | |
} |