001// *************************************************************************************************************************** 002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * 003// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * 004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * 005// * with the License. You may obtain a copy of the License at * 006// * * 007// * http://www.apache.org/licenses/LICENSE-2.0 * 008// * * 009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * 010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * 011// * specific language governing permissions and limitations under the License. * 012// *************************************************************************************************************************** 013package org.apache.juneau.internal; 014 015import static org.apache.juneau.internal.StringUtils.*; 016import static org.apache.juneau.internal.ThrowableUtils.*; 017 018import java.lang.reflect.*; 019import java.util.*; 020 021/** 022 * Quick and dirty utilities for working with arrays. 023 */ 024public final class ArrayUtils { 025 026 /** 027 * Appends one or more elements to an array. 028 * 029 * @param <T> The element type. 030 * @param array The array to append to. 031 * @param newElements The new elements to append to the array. 032 * @return A new array with the specified elements appended. 033 */ 034 @SuppressWarnings("unchecked") 035 public static <T> T[] append(T[] array, T...newElements) { 036 if (array == null) 037 return newElements; 038 if (newElements.length == 0) 039 return array; 040 T[] a = (T[])Array.newInstance(array.getClass().getComponentType(), array.length + newElements.length); 041 for (int i = 0; i < array.length; i++) 042 a[i] = array[i]; 043 for (int i = 0; i < newElements.length; i++) 044 a[i+array.length] = newElements[i]; 045 return a; 046 } 047 048 /** 049 * Appends one or more elements to an array. 050 * 051 * @param <T> The element type. 052 * @param array The array to append to. 053 * @param newElements The new elements to append to the array. 054 * @return A new array with the specified elements appended. 055 */ 056 @SuppressWarnings("unchecked") 057 public static <T> T[] append(T[] array, Collection<T> newElements) { 058 assertFieldNotNull(array, "array"); 059 if (newElements.size() == 0) 060 return array; 061 T[] a = (T[])Array.newInstance(array.getClass().getComponentType(), array.length + newElements.size()); 062 for (int i = 0; i < array.length; i++) 063 a[i] = array[i]; 064 int l = array.length; 065 for (T t : newElements) 066 a[l++] = t; 067 return a; 068 } 069 070 /** 071 * Combine an arbitrary number of arrays into a single array. 072 * 073 * @param arrays Collection of arrays to combine. 074 * @return A new combined array, or <jk>null</jk> if all arrays are <jk>null</jk>. 075 */ 076 @SuppressWarnings("unchecked") 077 public static <T> T[] combine(T[]...arrays) { 078 assertFieldNotNull(arrays, "arrays"); 079 int l = 0; 080 T[] a1 = null; 081 for (T[] a : arrays) { 082 if (a1 == null && a != null) 083 a1 = a; 084 l += (a == null ? 0 : a.length); 085 } 086 if (a1 == null) 087 return null; 088 T[] a = (T[])Array.newInstance(a1.getClass().getComponentType(), l); 089 int i = 0; 090 for (T[] aa : arrays) 091 if (aa != null) 092 for (T t : aa) 093 a[i++] = t; 094 return a; 095 } 096 097 /** 098 * Creates a new array with reversed entries. 099 * 100 * @param <T> The class type of the array. 101 * @param array The array to reverse. 102 * @return A new array with reversed entries, or <jk>null</jk> if the array was <jk>null</jk>. 103 */ 104 @SuppressWarnings("unchecked") 105 public static <T> T[] reverse(T[] array) { 106 if (array == null) 107 return null; 108 Class<T> c = (Class<T>)array.getClass().getComponentType(); 109 T[] a2 = (T[])Array.newInstance(c, array.length); 110 for (int i = 0; i < array.length; i++) 111 a2[a2.length-i-1] = array[i]; 112 return a2; 113 } 114 115 /** 116 * Sorts the elements in an array without creating a new array. 117 * 118 * @param array The array to sort. 119 * @return The same array. 120 */ 121 public static <T> T[] reverseInline(T[] array) { 122 if (array == null) 123 return null; 124 T t; 125 for (int i = 0, j = array.length-1; i < j; i++, j--) { 126 t = array[i]; 127 array[i] = array[j]; 128 array[j] = t; 129 } 130 return array; 131 } 132 133 /** 134 * Converts the specified array to a <code>Set</code>. 135 * 136 * <p> 137 * The order of the entries in the set are the same as the array. 138 * 139 * @param <T> The entry type of the array. 140 * @param array The array being wrapped in a <code>Set</code> interface. 141 * @return The new set. 142 */ 143 public static <T> Set<T> asSet(final T[] array) { 144 assertFieldNotNull(array, "array"); 145 return new AbstractSet<T>() { 146 147 @Override /* Set */ 148 public Iterator<T> iterator() { 149 return new Iterator<T>() { 150 int i = 0; 151 152 @Override /* Iterator */ 153 public boolean hasNext() { 154 return i < array.length; 155 } 156 157 @Override /* Iterator */ 158 public T next() { 159 if (i >= array.length) 160 throw new NoSuchElementException(); 161 T t = array[i]; 162 i++; 163 return t; 164 } 165 166 @Override /* Iterator */ 167 public void remove() { 168 throw new UnsupportedOperationException(); 169 } 170 }; 171 } 172 173 @Override /* Set */ 174 public int size() { 175 return array.length; 176 } 177 }; 178 } 179 180 /** 181 * Returns an iterator against an array. 182 * 183 * <p> 184 * This works with any array type (e.g. <code>String[]</code>, <code>Object[]</code>, 185 * <code><jk>int</jk>[]</code>, etc...). 186 * 187 * @param array The array to create an iterator over. 188 * @return An iterator over the specified array. 189 */ 190 public static Iterator<Object> iterator(final Object array) { 191 return new Iterator<Object>() { 192 int i = 0; 193 int length = array == null ? 0 : Array.getLength(array); 194 195 @Override /* Iterator */ 196 public boolean hasNext() { 197 return i < length; 198 } 199 200 @Override /* Iterator */ 201 public Object next() { 202 if (i >= length) 203 throw new NoSuchElementException(); 204 return Array.get(array, i++); 205 } 206 207 @Override /* Iterator */ 208 public void remove() { 209 throw new UnsupportedOperationException(); 210 } 211 }; 212 } 213 214 /** 215 * Converts the specified collection to an array. 216 * 217 * <p> 218 * Works on both object and primitive arrays. 219 * 220 * @param c The collection to convert to an array. 221 * @param componentType The component type of the collection. 222 * @return A new array. 223 */ 224 public static <T> Object toArray(Collection<?> c, Class<T> componentType) { 225 Object a = Array.newInstance(componentType, c.size()); 226 Iterator<?> it = c.iterator(); 227 int i = 0; 228 while (it.hasNext()) 229 Array.set(a, i++, it.next()); 230 return a; 231 } 232 233 /** 234 * Returns <jk>true</jk> if the specified object is an array. 235 * 236 * @param array The array to test. 237 * @return <jk>true</jk> if the specified object is an array. 238 */ 239 public static boolean isArray(Object array) { 240 return array != null && array.getClass().isArray(); 241 } 242 243 /** 244 * Converts the specified array to an <code>ArrayList</code> 245 * 246 * @param array The array to convert. 247 * @param componentType 248 * The type of objects in the array. 249 * It must match the actual component type in the array. 250 * @return A new {@link ArrayList} 251 */ 252 @SuppressWarnings("unchecked") 253 public static <T> List<T> toList(Object array, Class<T> componentType) { 254 List<T> l = new ArrayList<>(Array.getLength(array)); 255 for (int i = 0; i < Array.getLength(array); i++) 256 l.add((T)Array.get(array, i)); 257 return l; 258 } 259 260 /** 261 * Shortcut for calling <code>myList.toArray(new T[myList.size()]);</code> 262 * 263 * @param c The collection being converted to an array. 264 * @param componentType The component type of the array. 265 * @return The collection converted to an array. 266 */ 267 @SuppressWarnings("unchecked") 268 public static <T> T[] toObjectArray(Collection<?> c, Class<T> componentType) { 269 Object a = Array.newInstance(componentType, c.size()); 270 Iterator<?> it = c.iterator(); 271 int i = 0; 272 while (it.hasNext()) 273 Array.set(a, i++, it.next()); 274 return (T[])a; 275 } 276 277 /** 278 * Copies the specified array into the specified list. 279 * 280 * <p> 281 * Works on both object and primitive arrays. 282 * 283 * @param array The array to copy into a list. 284 * @param list The list to copy the values into. 285 * @return The same list passed in. 286 */ 287 @SuppressWarnings({"unchecked","rawtypes"}) 288 public static List copyToList(Object array, List list) { 289 if (array != null) { 290 int length = Array.getLength(array); 291 for (int i = 0; i < length; i++) 292 list.add(Array.get(array, i)); 293 } 294 return list; 295 } 296 297 /** 298 * Returns <jk>true</jk> if the specified array contains the specified element using the {@link Object#equals(Object)} 299 * method. 300 * 301 * @param element The element to check for. 302 * @param array The array to check. 303 * @return 304 * <jk>true</jk> if the specified array contains the specified element, 305 * <jk>false</jk> if the array or element is <jk>null</jk>. 306 */ 307 public static <T> boolean contains(T element, T[] array) { 308 return indexOf(element, array) != -1; 309 } 310 311 /** 312 * Returns the index position of the element in the specified array using the {@link Object#equals(Object)} method. 313 * 314 * @param element The element to check for. 315 * @param array The array to check. 316 * @return 317 * The index position of the element in the specified array, or <code>-1</code> if the array doesn't contain the 318 * element, or the array or element is <jk>null</jk>. 319 */ 320 public static <T> int indexOf(T element, T[] array) { 321 if (element == null) 322 return -1; 323 if (array == null) 324 return -1; 325 for (int i = 0; i < array.length; i++) 326 if (element.equals(array[i])) 327 return i; 328 return -1; 329 } 330 331 /** 332 * Returns <jk>true</jk> if the specified array contains the specified element using the {@link String#equals(Object)} 333 * method. 334 * 335 * @param element The element to check for. 336 * @param array The array to check. 337 * @return 338 * <jk>true</jk> if the specified array contains the specified element, 339 * <jk>false</jk> if the array or element is <jk>null</jk>. 340 */ 341 public static boolean contains(String element, String[] array) { 342 return indexOf(element, array) != -1; 343 } 344 345 /** 346 * Returns the index position of the element in the specified array using the {@link String#equals(Object)} method. 347 * 348 * @param element The element to check for. 349 * @param array The array to check. 350 * @return 351 * The index position of the element in the specified array, or 352 * <code>-1</code> if the array doesn't contain the element, or the array or element is <jk>null</jk>. 353 */ 354 public static int indexOf(String element, String[] array) { 355 if (element == null) 356 return -1; 357 if (array == null) 358 return -1; 359 for (int i = 0; i < array.length; i++) 360 if (element.equals(array[i])) 361 return i; 362 return -1; 363 } 364 365 /** 366 * Converts a primitive wrapper array (e.g. <code>Integer[]</code>) to a primitive array (e.g. <code><jk>int</jk>[]</code>). 367 * 368 * @param o The array to convert. Must be a primitive wrapper array. 369 * @return A new array. 370 * @throws IllegalArgumentException If object is not a wrapper object array. 371 */ 372 public static Object toPrimitiveArray(Object o) { 373 Class<?> c = o.getClass(); 374 if (! c.isArray()) 375 throw new IllegalArgumentException("Cannot pass non-array objects to toPrimitiveArray()"); 376 int l = Array.getLength(o); 377 Class<?> tc = ClassUtils.getPrimitiveForWrapper(c.getComponentType()); 378 if (tc == null) 379 throw new IllegalArgumentException("Array type is not a primitive wrapper array."); 380 Object a = Array.newInstance(tc, l); 381 for (int i = 0; i < l; i++) 382 Array.set(a, i, Array.get(o, i)); 383 return a; 384 } 385 386 /** 387 * Converts an Iterable to a list. 388 * 389 * @param i The iterable to convert. 390 * @return A new list of objects copied from the iterable. 391 */ 392 public static List<?> toList(Iterable<?> i) { 393 List<Object> l = new ArrayList<>(); 394 Iterator<?> i2 = i.iterator(); 395 while (i2.hasNext()) 396 l.add(i2.next()); 397 return l; 398 } 399 400 /** 401 * Returns the first object in the specified collection or array. 402 * 403 * @param val The collection or array object. 404 * @return 405 * The first object, or <jk>null</jk> if the collection or array is empty or <jk>null</jk> or the value 406 * isn't a collection or array. 407 */ 408 public static Object getFirst(Object val) { 409 if (val != null) { 410 if (val instanceof Collection) { 411 Collection<?> c = (Collection<?>)val; 412 if (c.isEmpty()) 413 return null; 414 return c.iterator().next(); 415 } 416 if (val.getClass().isArray()) 417 return Array.getLength(val) == 0 ? null : Array.get(val, 0); 418 } 419 return null; 420 } 421 422 /** 423 * Converts the specified collection to an array of strings. 424 * 425 * <p> 426 * Entries are converted to strings using {@link #toString()}. 427 * <jk>null</jk> values remain <jk>null</jk>. 428 * 429 * @param c The collection to convert. 430 * @return The collection as a string array. 431 */ 432 public static String[] toStringArray(Collection<?> c) { 433 String[] r = new String[c.size()]; 434 int i = 0; 435 for (Object o : c) 436 r[i++] = asString(o); 437 return r; 438 } 439 440 /** 441 * Returns <jk>true</jk> if the following sorted arrays are equals. 442 * 443 * @param a1 Array #1. 444 * @param a2 Array #2. 445 * @return <jk>true</jk> if the following sorted arrays are equals. 446 */ 447 public static boolean equals(String[] a1, String[] a2) { 448 if (a1.length != a2.length) 449 return false; 450 for (int i = 0; i < a1.length; i++) 451 if (! StringUtils.isEquals(a1[i], a2[i])) 452 return false; 453 return true; 454 } 455 456 /** 457 * Converts a collection to an array containing the elements in reversed order. 458 * 459 * @param c The component type of the array. 460 * @param l 461 * The collection to convert. 462 * <br>The collection is not modified. 463 * @return 464 * A new array, or <jk>null</jk> if the collection was <jk>null</jk>. 465 */ 466 @SuppressWarnings("unchecked") 467 public static <T> T[] toReverseArray(Class<T> c, Collection<T> l) { 468 if (l == null) 469 return null; 470 Object a = Array.newInstance(c, l.size()); 471 Iterator<T> i = l.iterator(); 472 int j = l.size(); 473 while (i.hasNext()) 474 Array.set(a, --j, i.next()); 475 return (T[])a; 476 } 477 478 /** 479 * Removes the specified element from the specified array. 480 * 481 * @param element The element to remove from the array. 482 * @param array The array to remove the element from. 483 * @return A new array with the element removed, or the original array if the array did not contain the element. 484 */ 485 public static Object[] remove(Object element, Object[] array) { 486 if (! contains(element, array)) 487 return array; 488 List<Object> l = new ArrayList<>(array.length); 489 for (Object o2 : array) { 490 if (! element.equals(o2)) 491 l.add(o2); 492 } 493 return l.toArray(new Object[l.size()]); 494 } 495}