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