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