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