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 java.lang.reflect.*;
016import java.util.*;
017
018import org.apache.juneau.*;
019import org.apache.juneau.json.*;
020
021/**
022 * Utility methods for collections.
023 */
024public final class CollectionUtils {
025
026   /**
027    * Reverses the order of a {@link LinkedHashMap}.
028    *
029    * @param in The map to reverse the order on.
030    * @return A new {@link LinkedHashMap} with keys in reverse order.
031    */
032   public static <K,V> LinkedHashMap<K,V> reverse(Map<K,V> in) {
033      if (in == null)
034         return null;
035      LinkedHashMap<K,V> m = new LinkedHashMap<>();
036
037      // Note:  Entry objects are reusable in an entry set, so we simply can't
038      // create a reversed iteration of that set.
039      List<K> keys = new ArrayList<>(in.keySet());
040      List<V> values = new ArrayList<>(in.values());
041      for (int i = in.size()-1; i >= 0; i--)
042         m.put(keys.get(i), values.get(i));
043
044      return m;
045   }
046
047   /**
048    * Add a value to a list if the value is not null.
049    *
050    * @param l The list to add to.
051    * @param o The element to add.
052    * @return The same list.
053    */
054   public static <T> List<T> addIfNotNull(List<T> l, T o) {
055      if (o != null)
056         l.add(o);
057      return l;
058   }
059
060   /**
061    * Adds the contents of one list to the other in reverse order.
062    *
063    * <p>
064    * i.e. add values from 2nd list from end-to-start order to the end of the 1st list.
065    *
066    * @param list The list to append to.
067    * @param append Contains the values to append to the list.
068    * @return The same list.
069    */
070   @SuppressWarnings({ "rawtypes", "unchecked" })
071   public static List<?> addReverse(List list, List append) {
072      for (ListIterator i = append.listIterator(append.size()); i.hasPrevious();)
073         list.add(i.previous());
074      return list;
075   }
076
077   /**
078    * Adds the contents of the array to the list in reverse order.
079    *
080    * <p>
081    * i.e. add values from the array from end-to-start order to the end of the list.
082    *
083    * @param list The list to append to.
084    * @param append Contains the values to append to the list.
085    * @return The same list.
086    */
087   @SuppressWarnings({ "rawtypes", "unchecked" })
088   public static List<?> addReverse(List list, Object[] append) {
089      for (int i = append.length - 1; i >= 0; i--)
090         list.add(append[i]);
091      return list;
092   }
093
094   /**
095    * Returns a reverse iterable of the specified collection.
096    *
097    * @param c The collection to iterate over.
098    * @return An iterable over the collection in reverse order.
099    */
100   public static <T> Iterable<T> reverseIterable(final Collection<T> c) {
101      return new Iterable<T>() {
102         @Override
103         public Iterator<T> iterator() {
104            if (c == null)
105               return Collections.EMPTY_LIST.iterator();
106            ArrayList<T> l = new ArrayList<>(c);
107            Collections.reverse(l);
108            return l.iterator();
109         }
110      };
111   }
112
113   /**
114    * Same as {@link Collections#reverse(List)}, but returns the list.
115    *
116    * @param l The list being reversed
117    * @return The same list.
118    */
119   public static <T> List<T> reverse(List<T> l) {
120      Collections.reverse(l);
121      return l;
122   }
123
124   /**
125    * Creates a new copy of a list in reverse order.
126    *
127    * @param l The old list.
128    * @return
129    *    A new list with reversed entries.
130    *    <br>Returns <jk>null</jk> if the list was <jk>null</jk>.
131    *    <br>Returns the same list if the list is empty.
132    */
133   public static <T> List<T> reverseCopy(List<T> l) {
134      if (l == null || l.isEmpty())
135         return l;
136      List<T> l2 = new ArrayList<>(l);
137      Collections.reverse(l2);
138      return l2;
139   }
140
141   /**
142    * Collapses a collection of individual objects, arrays, and collections into a single list of objects.
143    *
144    * @param o The collection of objects to collapse.
145    * @return A new linked-list of objects.
146    */
147   public static List<Object> collapse(Object...o) {
148      return collapse(new LinkedList<>(), o);
149   }
150
151   /**
152    * Same as {@link #collapse(Object...)} but allows you to supply your own list to append to.
153    *
154    * @param l The list to append to.
155    * @param o The collection of objects to collapse.
156    * @return The same list passed in.
157    */
158   @SuppressWarnings("unchecked")
159   public static List<Object> collapse(List<Object> l, Object...o) {
160      for (Object o2 : o) {
161         if (o2 != null) {
162            if (o2.getClass().isArray()) {
163               for (int i = 0; i < Array.getLength(o2); i++)
164                  collapse(l, Array.get(o2, i));
165            } else if (o2 instanceof Collection) {
166               for (Object o3 : (Collection<Object>)o2)
167                  collapse(l, o3);
168            } else {
169               l.add(o2);
170            }
171         }
172      }
173      return l;
174   }
175
176   /**
177    * Creates an immutable list from the specified collection.
178    *
179    * @param l The collection to copy from.
180    * @return An unmodifiable {@link ArrayList} copy of the collection, or a {@link Collections#emptyList()}
181    *    if the collection was empty or <jk>null</jk>.
182    */
183   public static <T> List<T> immutableList(Collection<T> l) {
184      if (l == null || l.isEmpty())
185         return Collections.emptyList();
186      return Collections.unmodifiableList(new ArrayList<>(l));
187   }
188
189   /**
190    * Creates an unmodifiable list from the specified collection.
191    *
192    * @param l The collection to copy from.
193    * @return An unmodifiable view of the list, or a {@link Collections#emptyList()}
194    *    if the list was empty or <jk>null</jk>.
195    */
196   public static <T> List<T> unmodifiableList(List<T> l) {
197      if (l == null || l.isEmpty())
198         return Collections.emptyList();
199      return Collections.unmodifiableList(l);
200   }
201
202   /**
203    * Creates an immutable list from the specified array.
204    *
205    * @param l The array to copy from.
206    * @return An unmodifiable {@link ArrayList} copy of the collection, or a {@link Collections#emptyList()}
207    *    if the collection was empty or <jk>null</jk>.
208    */
209   public static <T> List<T> immutableList(T[] l) {
210      if (l == null || l.length == 0)
211         return Collections.emptyList();
212      return Collections.unmodifiableList(new ArrayList<>(Arrays.asList(l)));
213   }
214
215   /**
216    * Creates an immutable map from the specified map.
217    *
218    * @param m The map to copy from.
219    * @return An unmodifiable {@link LinkedHashMap} copy of the collection, or a {@link Collections#emptyMap()}
220    *    if the collection was empty or <jk>null</jk>.
221    */
222   public static <K,V> Map<K,V> immutableMap(Map<K,V> m) {
223      if (m == null || m.isEmpty())
224         return Collections.emptyMap();
225      return Collections.unmodifiableMap(new LinkedHashMap<>(m));
226   }
227
228   /**
229    * Creates an unmodifiable map from the specified map.
230    *
231    * @param m The map to copy from.
232    * @return An unmodifiable view of the collection, or a {@link Collections#emptyMap()}
233    *    if the collection was empty or <jk>null</jk>.
234    */
235   public static <K,V> Map<K,V> unmodifiableMap(Map<K,V> m) {
236      if (m == null || m.isEmpty())
237         return Collections.emptyMap();
238      return Collections.unmodifiableMap(m);
239   }
240
241   /**
242    * Asserts that all entries in the list are either instances or subclasses of at least one of the specified classes.
243    *
244    * @param l The list to check.
245    * @param c The valid classes.
246    */
247   public static void assertTypes(List<Object> l, Class<?>...c) {
248      for (Object o : l) {
249         boolean matches = false;
250         if (o.getClass() == Class.class) {
251            Class<?> o2 = (Class<?>)o;
252            for (int i = 0; i < c.length && ! matches; i++)
253               matches = c[i].isAssignableFrom(o2);
254         } else {
255            for (int i = 0; i < c.length && ! matches; i++)
256               matches = c[i].isInstance(o);
257         }
258         if (! matches)
259            throw new FormattedRuntimeException("Invalid list entry ''{0}'' ({1}).  Not one of the following types: {2}", string(o), className(o), c);
260      }
261   }
262
263   static String string(Object value) {
264      return SimpleJsonSerializer.DEFAULT.toString(value);
265   }
266
267   static String className(Object value) {
268      return value.getClass().getSimpleName();
269   }
270
271}