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