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 <jk>true</jk> if the specified array contains the specified integer 313 * 314 * @param element The element to check for. 315 * @param array The array to check. 316 * @return 317 * <jk>true</jk> if the specified array contains the specified element, 318 * <jk>false</jk> if the array or element is <jk>null</jk>. 319 */ 320 public static boolean contains(int element, int[] array) { 321 if (array != null) 322 for (int i : array) 323 if (element == i) 324 return true; 325 return false; 326 } 327 328 /** 329 * Returns the index position of the element in the specified array using the {@link Object#equals(Object)} method. 330 * 331 * @param element The element to check for. 332 * @param array The array to check. 333 * @return 334 * The index position of the element in the specified array, or <code>-1</code> if the array doesn't contain the 335 * element, or the array or element is <jk>null</jk>. 336 */ 337 public static <T> int indexOf(T element, T[] array) { 338 if (element == null) 339 return -1; 340 if (array == null) 341 return -1; 342 for (int i = 0; i < array.length; i++) 343 if (element.equals(array[i])) 344 return i; 345 return -1; 346 } 347 348 /** 349 * Returns <jk>true</jk> if the specified array contains the specified element using the {@link String#equals(Object)} 350 * method. 351 * 352 * @param element The element to check for. 353 * @param array The array to check. 354 * @return 355 * <jk>true</jk> if the specified array contains the specified element, 356 * <jk>false</jk> if the array or element is <jk>null</jk>. 357 */ 358 public static boolean contains(String element, String[] array) { 359 return indexOf(element, array) != -1; 360 } 361 362 /** 363 * Returns the index position of the element in the specified array using the {@link String#equals(Object)} method. 364 * 365 * @param element The element to check for. 366 * @param array The array to check. 367 * @return 368 * The index position of the element in the specified array, or 369 * <code>-1</code> if the array doesn't contain the element, or the array or element is <jk>null</jk>. 370 */ 371 public static int indexOf(String element, String[] array) { 372 if (element == null) 373 return -1; 374 if (array == null) 375 return -1; 376 for (int i = 0; i < array.length; i++) 377 if (element.equals(array[i])) 378 return i; 379 return -1; 380 } 381 382 /** 383 * Converts a primitive wrapper array (e.g. <code>Integer[]</code>) to a primitive array (e.g. <code><jk>int</jk>[]</code>). 384 * 385 * @param o The array to convert. Must be a primitive wrapper array. 386 * @return A new array. 387 * @throws IllegalArgumentException If object is not a wrapper object array. 388 */ 389 public static Object toPrimitiveArray(Object o) { 390 Class<?> c = o.getClass(); 391 if (! c.isArray()) 392 throw new IllegalArgumentException("Cannot pass non-array objects to toPrimitiveArray()"); 393 int l = Array.getLength(o); 394 Class<?> tc = ClassUtils.getPrimitiveForWrapper(c.getComponentType()); 395 if (tc == null) 396 throw new IllegalArgumentException("Array type is not a primitive wrapper array."); 397 Object a = Array.newInstance(tc, l); 398 for (int i = 0; i < l; i++) 399 Array.set(a, i, Array.get(o, i)); 400 return a; 401 } 402 403 /** 404 * Converts an Iterable to a list. 405 * 406 * @param i The iterable to convert. 407 * @return A new list of objects copied from the iterable. 408 */ 409 public static List<?> toList(Iterable<?> i) { 410 List<Object> l = new ArrayList<>(); 411 Iterator<?> i2 = i.iterator(); 412 while (i2.hasNext()) 413 l.add(i2.next()); 414 return l; 415 } 416 417 /** 418 * Returns the first object in the specified collection or array. 419 * 420 * @param val The collection or array object. 421 * @return 422 * The first object, or <jk>null</jk> if the collection or array is empty or <jk>null</jk> or the value 423 * isn't a collection or array. 424 */ 425 public static Object getFirst(Object val) { 426 if (val != null) { 427 if (val instanceof Collection) { 428 Collection<?> c = (Collection<?>)val; 429 if (c.isEmpty()) 430 return null; 431 return c.iterator().next(); 432 } 433 if (val.getClass().isArray()) 434 return Array.getLength(val) == 0 ? null : Array.get(val, 0); 435 } 436 return null; 437 } 438 439 /** 440 * Converts the specified collection to an array of strings. 441 * 442 * <p> 443 * Entries are converted to strings using {@link #toString()}. 444 * <jk>null</jk> values remain <jk>null</jk>. 445 * 446 * @param c The collection to convert. 447 * @return The collection as a string array. 448 */ 449 public static String[] toStringArray(Collection<?> c) { 450 String[] r = new String[c.size()]; 451 int i = 0; 452 for (Object o : c) 453 r[i++] = asString(o); 454 return r; 455 } 456 457 /** 458 * Returns <jk>true</jk> if the following sorted arrays are equals. 459 * 460 * @param a1 Array #1. 461 * @param a2 Array #2. 462 * @return <jk>true</jk> if the following sorted arrays are equals. 463 */ 464 public static boolean equals(String[] a1, String[] a2) { 465 if (a1.length != a2.length) 466 return false; 467 for (int i = 0; i < a1.length; i++) 468 if (! StringUtils.isEquals(a1[i], a2[i])) 469 return false; 470 return true; 471 } 472 473 /** 474 * Converts a collection to an array containing the elements in reversed order. 475 * 476 * @param c The component type of the array. 477 * @param l 478 * The collection to convert. 479 * <br>The collection is not modified. 480 * @return 481 * A new array, or <jk>null</jk> if the collection was <jk>null</jk>. 482 */ 483 @SuppressWarnings("unchecked") 484 public static <T> T[] toReverseArray(Class<T> c, Collection<T> l) { 485 if (l == null) 486 return null; 487 Object a = Array.newInstance(c, l.size()); 488 Iterator<T> i = l.iterator(); 489 int j = l.size(); 490 while (i.hasNext()) 491 Array.set(a, --j, i.next()); 492 return (T[])a; 493 } 494 495 /** 496 * Removes the specified element from the specified array. 497 * 498 * @param element The element to remove from the array. 499 * @param array The array to remove the element from. 500 * @return A new array with the element removed, or the original array if the array did not contain the element. 501 */ 502 public static Object[] remove(Object element, Object[] array) { 503 if (! contains(element, array)) 504 return array; 505 List<Object> l = new ArrayList<>(array.length); 506 for (Object o2 : array) { 507 if (! element.equals(o2)) 508 l.add(o2); 509 } 510 return l.toArray(new Object[l.size()]); 511 } 512}