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.common.internal.ArgUtils.*;
016import static org.apache.juneau.common.internal.StringUtils.*;
017
018import java.lang.reflect.*;
019import java.util.*;
020
021import org.apache.juneau.common.internal.*;
022
023/**
024 * Quick and dirty utilities for working with arrays.
025 *
026 * <h5 class='section'>See Also:</h5><ul>
027
028 * </ul>
029 */
030public final class ArrayUtils {
031
032   /**
033    * Appends one or more elements to an array.
034    *
035    * @param <T> The element type.
036    * @param array The array to append to.
037    * @param newElements The new elements to append to the array.
038    * @return A new array with the specified elements appended.
039    */
040   @SuppressWarnings("unchecked")
041   public static <T> T[] append(T[] array, T...newElements) {
042      if (array == null)
043         return newElements;
044      if (newElements.length == 0)
045         return array;
046      T[] a = (T[])Array.newInstance(array.getClass().getComponentType(), array.length + newElements.length);
047      for (int i = 0; i < array.length; i++)
048         a[i] = array[i];
049      for (int i = 0; i < newElements.length; i++)
050         a[i+array.length] = newElements[i];
051      return a;
052   }
053
054   /**
055    * Combine an arbitrary number of arrays into a single array.
056    *
057    * @param <E> The element type.
058    * @param arrays Collection of arrays to combine.
059    * @return A new combined array, or <jk>null</jk> if all arrays are <jk>null</jk>.
060    */
061   @SuppressWarnings("unchecked")
062   public static <E> E[] combine(E[]...arrays) {
063      assertArgNotNull("arrays", arrays);
064      int l = 0;
065      E[] a1 = null;
066      for (E[] a : arrays) {
067         if (a1 == null && a != null)
068            a1 = a;
069         l += (a == null ? 0 : a.length);
070      }
071      if (a1 == null)
072         return null;
073      E[] a = (E[])Array.newInstance(a1.getClass().getComponentType(), l);
074      int i = 0;
075      for (E[] aa : arrays)
076         if (aa != null)
077            for (E t : aa)
078               a[i++] = t;
079      return a;
080   }
081
082   /**
083    * Converts the specified array to a <c>Set</c>.
084    *
085    * <p>
086    * The order of the entries in the set are the same as the array.
087    *
088    * @param <T> The entry type of the array.
089    * @param array The array being wrapped in a <c>Set</c> interface.
090    * @return The new set.
091    */
092   public static <T> Set<T> asSet(final T[] array) {
093      assertArgNotNull("array", array);
094      return new AbstractSet<>() {
095
096         @Override /* Set */
097         public Iterator<T> iterator() {
098            return new Iterator<>() {
099               int i = 0;
100
101               @Override /* Iterator */
102               public boolean hasNext() {
103                  return i < array.length;
104               }
105
106               @Override /* Iterator */
107               public T next() {
108                  if (i >= array.length)
109                     throw new NoSuchElementException();
110                  T t = array[i];
111                  i++;
112                  return t;
113               }
114
115               @Override /* Iterator */
116               public void remove() {
117                  throw new UnsupportedOperationException("Not supported.");
118               }
119            };
120         }
121
122         @Override /* Set */
123         public int size() {
124            return array.length;
125         }
126      };
127   }
128
129   /**
130    * Converts the specified collection to an array.
131    *
132    * <p>
133    * Works on both object and primitive arrays.
134    *
135    * @param <E> The element type.
136    * @param c The collection to convert to an array.
137    * @param elementType The component type of the collection.
138    * @return A new array.
139    */
140   public static <E> Object toArray(Collection<?> c, Class<E> elementType) {
141      Object a = Array.newInstance(elementType, c.size());
142      Iterator<?> it = c.iterator();
143      int i = 0;
144      while (it.hasNext())
145         Array.set(a, i++, it.next());
146      return a;
147   }
148
149   /**
150    * Returns <jk>true</jk> if the specified object is an array.
151    *
152    * @param array The array to test.
153    * @return <jk>true</jk> if the specified object is an array.
154    */
155   public static boolean isArray(Object array) {
156      return array != null && array.getClass().isArray();
157   }
158
159   /**
160    * Converts the specified array to an <c>ArrayList</c>
161    *
162    * @param <E> The element type.
163    * @param array The array to convert.
164    * @param elementType
165    *    The type of objects in the array.
166    *    It must match the actual component type in the array.
167    * @return A new {@link ArrayList}
168    */
169   @SuppressWarnings("unchecked")
170   public static <E> List<E> toList(Object array, Class<E> elementType) {
171      List<E> l = new ArrayList<>(Array.getLength(array));
172      for (int i = 0; i < Array.getLength(array); i++)
173         l.add((E)Array.get(array, i));
174      return l;
175   }
176
177   /**
178    * Recursively converts the specified array into a list of objects.
179    *
180    * @param array The array to convert.
181    * @return A new {@link ArrayList}
182    */
183   public static List<Object> toObjectList(Object array) {
184      List<Object> l = new ArrayList<>(Array.getLength(array));
185      for (int i = 0; i < Array.getLength(array); i++) {
186         Object o = Array.get(array, i);
187         if (o != null && o.getClass().isArray())
188            o = toObjectList(o);
189         l.add(o);
190      }
191      return l;
192   }
193
194   /**
195    * Copies the specified array into the specified list.
196    *
197    * <p>
198    * Works on both object and primitive arrays.
199    *
200    * @param array The array to copy into a list.
201    * @param list The list to copy the values into.
202    * @return The same list passed in.
203    */
204   @SuppressWarnings({"unchecked","rawtypes"})
205   public static List copyToList(Object array, List list) {
206      if (array != null) {
207         int length = Array.getLength(array);
208         for (int i = 0; i < length; i++)
209            list.add(Array.get(array, i));
210      }
211      return list;
212   }
213
214   /**
215    * Returns <jk>true</jk> if the specified array contains the specified element using the {@link String#equals(Object)}
216    * method.
217    *
218    * @param element The element to check for.
219    * @param array The array to check.
220    * @return
221    *    <jk>true</jk> if the specified array contains the specified element,
222    *    <jk>false</jk> if the array or element is <jk>null</jk>.
223    */
224   public static boolean contains(String element, String[] array) {
225      return indexOf(element, array) != -1;
226   }
227
228   /**
229    * Returns the index position of the element in the specified array using the {@link String#equals(Object)} method.
230    *
231    * @param element The element to check for.
232    * @param array The array to check.
233    * @return
234    *    The index position of the element in the specified array, or
235    *    <c>-1</c> if the array doesn't contain the element, or the array or element is <jk>null</jk>.
236    */
237   public static int indexOf(String element, String[] array) {
238      if (element == null || array == null)
239         return -1;
240      for (int i = 0; i < array.length; i++)
241         if (element.equals(array[i]))
242            return i;
243      return -1;
244   }
245
246   /**
247    * Converts the specified collection to an array of strings.
248    *
249    * <p>
250    * Entries are converted to strings using {@link #toString()}.
251    * <jk>null</jk> values remain <jk>null</jk>.
252    *
253    * @param c The collection to convert.
254    * @return The collection as a string array.
255    */
256   public static String[] toStringArray(Collection<?> c) {
257      String[] r = new String[c.size()];
258      int i = 0;
259      for (Object o : c)
260         r[i++] = stringify(o);
261      return r;
262   }
263
264   /**
265    * Returns <jk>true</jk> if the following sorted arrays are equals.
266    *
267    * @param a1 Array #1.
268    * @param a2 Array #2.
269    * @return <jk>true</jk> if the following sorted arrays are equals.
270    */
271   public static boolean equals(String[] a1, String[] a2) {
272      if (a1.length != a2.length)
273         return false;
274      for (int i = 0; i < a1.length; i++)
275         if (! StringUtils.eq(a1[i], a2[i]))
276            return false;
277      return true;
278   }
279
280   /**
281    * Makes a copy of the specified array.
282    *
283    * @param array The array to copy.
284    * @param <T> The element type.
285    * @return A new copy of the array, or <jk>null</jk> if the array was <jk>null</jk>.s
286    */
287   public static <T> T[] copyOf(T[] array) {
288      return array == null ? null : Arrays.copyOf(array, array.length);
289   }
290
291   /**
292    * Returns <jk>true</jk> if the specified array is not null and has a length greater than zero.
293    *
294    * @param array The array to check.
295    * @return <jk>true</jk> if the specified array is not null and has a length greater than zero.
296    */
297   public static boolean isNotEmptyArray(Object[] array) {
298      return array != null && array.length > 0;
299   }
300
301   /**
302    * Returns <jk>true</jk> if the specified array is null or has a length of zero.
303    *
304    * @param array The array to check.
305    * @return <jk>true</jk> if the specified array is null or has a length of zero.
306    */
307   public static boolean isEmptyArray(Object[] array) {
308      return array == null || array.length == 0;
309   }
310
311   /**
312    * Returns <jk>true</jk> if both specified arrays are null or have a length of zero.
313    *
314    * @param array1 The array to check.
315    * @param array2 The array to check.
316    * @return <jk>true</jk> if the specified array is null or has a length of zero.
317    */
318   public static boolean isEmptyArray(Object[] array1, Object[] array2) {
319      return isEmptyArray(array1) && isEmptyArray(array2);
320   }
321
322   /**
323    * Reverses the entries in an array.
324    *
325    * @param <E> The element type.
326    * @param array The array to reverse.
327    * @return The same array.
328    */
329   public static <E> E[] reverse(E[] array) {
330      for (int i = 0; i < array.length / 2; i++) {
331         E temp = array[i];
332         array[i] = array[array.length - i - 1];
333         array[array.length - i - 1] = temp;
334      }
335      return array;
336   }
337}