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.ObjectUtils.*;
017
018import java.lang.reflect.*;
019import java.util.*;
020
021import org.apache.juneau.*;
022import org.apache.juneau.parser.*;
023
024/**
025 * Utility methods for collections.
026 */
027public final class CollectionUtils {
028
029   /**
030    * Add a value to a list if the value is not null.
031    *
032    * @param l The list to add to.
033    * @param o The element to add.
034    * @return The same list.
035    */
036   public static <T> List<T> addIfNotNull(List<T> l, T o) {
037      if (o != null)
038         l.add(o);
039      return l;
040   }
041
042   /**
043    * Adds the contents of one list to the other in reverse order.
044    *
045    * <p>
046    * i.e. add values from 2nd list from end-to-start order to the end of the 1st list.
047    *
048    * @param list The list to append to.
049    * @param append Contains the values to append to the list.
050    * @return The same list.
051    */
052   @SuppressWarnings({ "rawtypes", "unchecked" })
053   public static List<?> addReverse(List list, List append) {
054      for (ListIterator i = append.listIterator(append.size()); i.hasPrevious();)
055         list.add(i.previous());
056      return list;
057   }
058
059   /**
060    * Adds the contents of the array to the list in reverse order.
061    *
062    * <p>
063    * i.e. add values from the array from end-to-start order to the end of the list.
064    *
065    * @param list The list to append to.
066    * @param append Contains the values to append to the list.
067    * @return The same list.
068    */
069   @SuppressWarnings({ "rawtypes", "unchecked" })
070   public static List<?> addReverse(List list, Object[] append) {
071      for (int i = append.length - 1; i >= 0; i--)
072         list.add(append[i]);
073      return list;
074   }
075
076   /**
077    * Returns an iterable over the specified collection.
078    *
079    * @param c The collection to iterate over.
080    * @param reverse If <jk>true</jk>, iterate in reverse order.
081    * @return An iterable over the collection.
082    */
083   public static <T> Iterable<T> iterable(final Collection<T> c, boolean reverse) {
084      if (reverse)
085         return new ReverseIterable<>(c instanceof List ? (List<T>)c : new ArrayList<>(c));
086      return c;
087   }
088
089   /**
090    * Returns an iterable over the specified list.
091    *
092    * @param c The collection to iterate over.
093    * @param reverse If <jk>true</jk>, iterate in reverse order.
094    * @return An iterable over the collection.
095    */
096   public static <T> Iterable<T> iterable(final List<T> c, boolean reverse) {
097      if (reverse)
098         return new ReverseIterable<>(c);
099      return c;
100   }
101
102   /**
103    * Returns an iterable over the specified array.
104    *
105    * @param c The collection to iterate over.
106    * @param reverse If <jk>true</jk>, iterate in reverse order.
107    * @return An iterable over the collection.
108    */
109   public static <T> Iterable<T> iterable(T[] c, boolean reverse) {
110      if (reverse)
111         return new ReverseIterable<>(Arrays.asList(c));
112      return Arrays.asList(c);
113   }
114
115   /**
116    * Returns an iterable over the specified enumeration.
117    *
118    * @param e The collection to iterate over.
119    * @return An iterable over the enumeration.
120    */
121   public static <E> Iterable<E> iterable(final Enumeration<E> e) {
122      if (e == null)
123         return null;
124      return new Iterable<E>() {
125         @Override
126         public Iterator<E> iterator() {
127            return new Iterator<E>() {
128               @Override
129               public boolean hasNext() {
130                  return e.hasMoreElements();
131               }
132               @Override
133               public E next() {
134                  return e.nextElement();
135               }
136               @Override
137               public void remove() {
138                  throw new UnsupportedOperationException();
139               }
140            };
141         }
142      };
143   }
144
145   /**
146    * Creates an unmodifiable list from the specified list.
147    *
148    * @param l The collection to copy from.
149    * @return An unmodifiable view of the list, or a {@link Collections#emptyList()}
150    *    if the list was empty or <jk>null</jk>.
151    */
152   public static <T> List<T> unmodifiableList(List<T> l) {
153      if (l == null || l.isEmpty())
154         return Collections.emptyList();
155      return Collections.unmodifiableList(l);
156   }
157
158   /**
159    * Creates an unmodifiable list from the specified array.
160    *
161    * @param l The collection to copy from.
162    * @return An unmodifiable view of the list, or a {@link Collections#emptyList()}
163    *    if the list was empty or <jk>null</jk>.
164    */
165   public static <T> List<T> unmodifiableList(T[] l) {
166      if (l == null || l.length == 0)
167         return Collections.emptyList();
168      return Collections.unmodifiableList(Arrays.asList(l));
169   }
170
171   /**
172    * Creates an immutable list from the specified collection.
173    *
174    * @param l The collection to copy from.
175    * @return An unmodifiable {@link ArrayList} copy of the collection, or a {@link Collections#emptyList()}
176    *    if the collection was empty or <jk>null</jk>.
177    */
178   public static <T> List<T> immutableList(Collection<T> l) {
179      if (l == null || l.isEmpty())
180         return Collections.emptyList();
181      return Collections.unmodifiableList(new ArrayList<>(l));
182   }
183
184   /**
185    * Creates an immutable list from the specified array.
186    *
187    * @param l The array to copy from.
188    * @return An unmodifiable {@link ArrayList} copy of the collection, or a {@link Collections#emptyList()}
189    *    if the collection was empty or <jk>null</jk>.
190    */
191   public static <T> List<T> immutableList(T[] l) {
192      if (l == null || l.length == 0)
193         return Collections.emptyList();
194      return Collections.unmodifiableList(new ArrayList<>(Arrays.asList(l)));
195   }
196
197   /**
198    * Creates an immutable map from the specified map.
199    *
200    * @param m The map to copy from.
201    * @return An unmodifiable {@link LinkedHashMap} copy of the collection, or a {@link Collections#emptyMap()}
202    *    if the collection was empty or <jk>null</jk>.
203    */
204   public static <K,V> Map<K,V> immutableMap(Map<K,V> m) {
205      if (m == null || m.isEmpty())
206         return Collections.emptyMap();
207      return Collections.unmodifiableMap(new LinkedHashMap<>(m));
208   }
209
210   /**
211    * Creates an unmodifiable map from the specified map.
212    *
213    * @param m The map to copy from.
214    * @return An unmodifiable view of the collection, or a {@link Collections#emptyMap()}
215    *    if the collection was empty or <jk>null</jk>.
216    */
217   public static <K,V> Map<K,V> unmodifiableMap(Map<K,V> m) {
218      if (m == null || m.isEmpty())
219         return Collections.emptyMap();
220      return Collections.unmodifiableMap(m);
221   }
222
223   /**
224    * Adds a set of values to an existing list.
225    *
226    * @param appendTo
227    *    The list to append to.
228    *    <br>If <jk>null</jk>, a new {@link ArrayList} will be created.
229    * @param values The values to add.
230    * @param type The data type of the elements.
231    * @param args The generic type arguments of the data type.
232    * @return The converted value, or <jk>null</jk> if the input was null.
233    */
234   public static <T> List<T> addToList(List<T> appendTo, Object[] values, Class<T> type, Type...args) {
235      if (values == null)
236         return appendTo;
237      try {
238         List<T> l = appendTo;
239         if (appendTo == null)
240            l = new ArrayList<>();
241         for (Object o : values) {
242            if (o != null) {
243               if (isObjectList(o, false)) {
244                  for (Object o2 : new ObjectList(o.toString()))
245                     l.add(toType(o2, type, args));
246               } else if (o instanceof Collection) {
247                  for (Object o2 : (Collection<?>)o)
248                     l.add(toType(o2, type, args));
249               } else if (o.getClass().isArray()) {
250                  for (int i = 0; i < Array.getLength(o); i++)
251                     l.add(toType(Array.get(o, i), type, args));
252               } else {
253                  l.add(toType(o, type, args));
254               }
255            }
256         }
257         return l.isEmpty() ? null : l;
258      } catch (ParseException e) {
259         throw new RuntimeException(e);
260      }
261   }
262
263   /**
264    * Adds a set of values to an existing map.
265    *
266    * @param appendTo
267    *    The map to append to.
268    *    <br>If <jk>null</jk>, a new {@link LinkedHashMap} will be created.
269    * @param values The values to add.
270    * @param keyType The data type of the keys.
271    * @param valueType The data type of the values.
272    * @param valueTypeArgs The generic type arguments of the data type of the values.
273    * @return The converted value, or <jk>null</jk> if the input was null.
274    */
275   @SuppressWarnings("unchecked")
276   public static <K,V> Map<K,V> addToMap(Map<K,V> appendTo, Object[] values, Class<K> keyType, Class<V> valueType, Type...valueTypeArgs) {
277      if (values == null)
278         return appendTo;
279      try {
280         Map<K,V> m = appendTo;
281         if (m == null)
282            m = new LinkedHashMap<>();
283         for (Object o : values) {
284            if (o != null) {
285               if (isObjectMap(o, false)) {
286                  for (Map.Entry<String,Object> e : new ObjectMap(o.toString()).entrySet())
287                     m.put(toType(e.getKey(), keyType), toType(e.getValue(), valueType, valueTypeArgs));
288               } else if (o instanceof Map) {
289                  for (Map.Entry<Object,Object> e : ((Map<Object,Object>)o).entrySet())
290                     m.put(toType(e.getKey(), keyType), toType(e.getValue(), valueType, valueTypeArgs));
291               } else {
292                  throw new FormattedRuntimeException("Invalid object type {0} passed to addToMap()", o.getClass().getName());
293               }
294            }
295         }
296         return m.isEmpty() ? null : m;
297      } catch (ParseException e) {
298         throw new RuntimeException(e);
299      }
300   }
301
302   /**
303    * Creates a new list from the specified collection.
304    *
305    * @param val The value to copy from.
306    * @return A new {@link ArrayList}, or <jk>null</jk> if the input was null.
307    */
308   public static <T> List<T> newList(Collection<T> val) {
309      if (val == null)
310         return null;
311      return new ArrayList<>(val);
312   }
313
314   /**
315    * Copies the specified values into an existing list.
316    *
317    * @param l
318    *    The list to add to.
319    *    <br>If <jk>null</jk>, a new {@link ArrayList} will be created.
320    * @param val The values to add.
321    * @return The list with values copied into it.
322    */
323   public static <T> List<T> addToList(List<T> l, Collection<T> val) {
324      if (val != null) {
325         if (l == null)
326            l = new ArrayList<>(val);
327         else
328            l.addAll(val);
329      }
330      return l;
331   }
332
333   /**
334    * Creates a new map from the specified map.
335    *
336    * @param val The value to copy from.
337    * @return A new {@link LinkedHashMap}, or <jk>null</jk> if the input was null.
338    */
339   public static <K,V> Map<K,V> newMap(Map<K,V> val) {
340      if (val == null)
341         return null;
342      return new LinkedHashMap<>(val);
343   }
344
345   /**
346    * Copies the specified values into an existing map.
347    *
348    * @param m
349    *    The map to add to.
350    *    <br>If <jk>null</jk>, a new {@link LinkedHashMap} will be created.
351    * @param val The values to add.
352    * @return The list with values copied into it.
353    */
354   public static <K,V> Map<K,V> addToMap(Map<K,V> m, Map<K,V> val) {
355      if (val != null) {
356         if (m == null)
357            m = new LinkedHashMap<>(val);
358         else
359            m.putAll(val);
360      }
361      return m;
362   }
363
364   /**
365    * Adds a single entry into an existing map.
366    *
367    * @param m
368    *    The map to add to.
369    *    <br>If <jk>null</jk>, a new {@link LinkedHashMap} will be created.
370    * @param key The entry key.
371    * @param value The entry value.
372    * @return The list with values copied into it.
373    */
374   public static <K,V> Map<K,V> addToMap(Map<K,V> m, K key, V value) {
375      if (m == null)
376         m = new LinkedHashMap<>();
377      m.put(key, value);
378      return m;
379   }
380
381   /**
382    * Creates a new map from the specified map.
383    *
384    * @param val The value to copy from.
385    * @param comparator The key comparator to use, or <jk>null</jk> to use natural ordering.
386    * @return A new {@link LinkedHashMap}, or <jk>null</jk> if the input was null.
387    */
388   public static <K,V> Map<K,V> newSortedMap(Map<K,V> val, Comparator<K> comparator) {
389      if (val == null)
390         return null;
391      Map<K,V> m = new TreeMap<>(comparator);
392      m.putAll(val);
393      return m;
394   }
395
396   /**
397    * Creates a case-insensitive ordered set out of the specified string values.
398    *
399    * @param values The values to populate the set with.
400    * @return A new ordered set.
401    */
402   public static Set<String> newSortedCaseInsensitiveSet(String...values) {
403      Set<String> s = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER) {
404         private static final long serialVersionUID = 1L;
405         @Override
406         public boolean contains(Object v) {
407            return v == null ? false : super.contains(v);
408         }
409      };
410      for (String v : values)
411         if (v != null)
412            s.add(v);
413      return s;
414   }
415
416   /**
417    * Creates a case-insensitive ordered set out of the specified string values.
418    *
419    * @param values
420    *    A comma-delimited list of the values to populate the set with.
421    * @return A new ordered set.
422    */
423   public static Set<String> newSortedCaseInsensitiveSet(String values) {
424      return newSortedCaseInsensitiveSet(StringUtils.split(StringUtils.emptyIfNull(values)));
425   }
426
427   /**
428    * Same as {@link #newSortedCaseInsensitiveSet(String)} but makes the set unmodifiable.
429    *
430    * @param values
431    *    A comma-delimited list of the values to populate the set with.
432    * @return A new ordered set.
433    */
434   public static Set<String> newUnmodifiableSortedCaseInsensitiveSet(String values) {
435      return Collections.unmodifiableSet(newSortedCaseInsensitiveSet(StringUtils.split(StringUtils.emptyIfNull(values))));
436   }
437
438   /**
439    * Copies the specified values into an existing map.
440    *
441    * @param m
442    *    The map to add to.
443    *    <br>If <jk>null</jk>, a new {@link LinkedHashMap} will be created.
444    * @param val The values to add.
445    * @param comparator The key comparator to use, or <jk>null</jk> to use natural ordering.
446    * @return The list with values copied into it.
447    */
448   public static <K,V> Map<K,V> addToSortedMap(Map<K,V> m, Map<K,V> val, Comparator<K> comparator) {
449      if (val != null) {
450         if (m == null) {
451            m = new TreeMap<>(comparator);
452            m.putAll(val);
453         } else {
454            m.putAll(val);
455         }
456      }
457      return m;
458   }
459
460   /**
461    * Adds a single entry into an existing map.
462    *
463    * @param m
464    *    The map to add to.
465    *    <br>If <jk>null</jk>, a new {@link LinkedHashMap} will be created.
466    * @param key The entry key.
467    * @param value The entry value.
468    * @param comparator The key comparator to use, or <jk>null</jk> to use natural ordering.
469    * @return The list with values copied into it.
470    */
471   public static <K,V> Map<K,V> addToSortedMap(Map<K,V> m, K key, V value, Comparator<K> comparator) {
472      if (m == null)
473         m = new TreeMap<>(comparator);
474      m.put(key, value);
475      return m;
476   }
477
478   /**
479    * Converts the specified arguments into an unmodifiable {@link HashSet}.
480    *
481    * @param values The entries to populate the hashset with.
482    * @return A new {@link HashSet} populated with the specified arguments.
483    */
484   @SafeVarargs
485   public static <T> Set<T> newUnmodifiableHashSet(T...values) {
486      return Collections.unmodifiableSet(new HashSet<>(Arrays.asList(values)));
487   }
488}