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