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}