001/*
002 * $Id: CollectionHelpers.java,v 1.21 2011/04/06 19:59:45 jbeavers Exp $
003 *
004 * This file is part of McIDAS-V
005 *
006 * Copyright 2007-2011
007 * Space Science and Engineering Center (SSEC)
008 * University of Wisconsin - Madison
009 * 1225 W. Dayton Street, Madison, WI 53706, USA
010 * https://www.ssec.wisc.edu/mcidas
011 * 
012 * All Rights Reserved
013 * 
014 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and
015 * some McIDAS-V source code is based on IDV and VisAD source code.  
016 * 
017 * McIDAS-V is free software; you can redistribute it and/or modify
018 * it under the terms of the GNU Lesser Public License as published by
019 * the Free Software Foundation; either version 3 of the License, or
020 * (at your option) any later version.
021 * 
022 * McIDAS-V is distributed in the hope that it will be useful,
023 * but WITHOUT ANY WARRANTY; without even the implied warranty of
024 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
025 * GNU Lesser Public License for more details.
026 * 
027 * You should have received a copy of the GNU Lesser Public License
028 * along with this program.  If not, see http://www.gnu.org/licenses.
029 */
030
031package edu.wisc.ssec.mcidasv.util;
032
033import java.lang.reflect.Array;
034import java.util.ArrayList;
035import java.util.Collection;
036import java.util.Collections;
037import java.util.HashMap;
038import java.util.HashSet;
039import java.util.Iterator;
040import java.util.LinkedHashMap;
041import java.util.LinkedHashSet;
042import java.util.List;
043import java.util.Map;
044import java.util.Set;
045import java.util.concurrent.ConcurrentHashMap;
046import java.util.concurrent.CopyOnWriteArrayList;
047import java.util.concurrent.CopyOnWriteArraySet;
048
049import edu.wisc.ssec.mcidasv.util.functional.Function;
050
051/**
052 * A <i>collection</i> (ugh) of static methods that make working with Java's 
053 * default collections a bit easier, or at least allows you to elide some of
054 * the redundancy of idiomatic Java.
055 * 
056 * <p>Make use of {@literal "static imports"} to omit even more needless code.
057 */
058@SuppressWarnings({"ClassWithoutLogger", "UnusedDeclaration"})
059public final class CollectionHelpers {
060
061    /** Never! */
062    private CollectionHelpers() {}
063
064    /**
065     * {@literal "Converts"} the incoming {@literal "varargs"} into an array.
066     * 
067     * <p>Useful for doing things like:
068     * {@code String[] strs = arr("hello", "how", "are", "you?");}
069     * 
070     * @param ts Items that will make up the elements of the returned array.
071     * Cannot be {@code null}, and (for now) the items should be of the 
072     * <i>same</i> type.
073     * 
074     * @return An array populated with each item from {@code ts}.
075     */
076    public static <T> T[] arr(T... ts) { 
077        return ts;
078    }
079
080    /**
081     * Creates a {@link List} from incoming {@literal "varargs"}. Currently 
082     * uses {@link ArrayList} as the {@code List} implementation.
083     * 
084     * <p>Used like so:
085     * {@code List<String> listy = list("y", "helo", "thar");}
086     * 
087     * @param elements Items that will make up the elements of the returned
088     * {@code List}.
089     * 
090     * @return A {@code List} whose elements are each item within {@code elements}.
091     */
092    public static <E> List<E> list(E... elements) {
093        List<E> newList = arrList(elements.length);
094        Collections.addAll(newList, elements);
095        return newList;
096    }
097
098    /**
099     * Creates a {@link Set} from incoming {@literal "varargs"}. Currently uses
100     * {@link LinkedHashSet} as the {@code Set} implementation (to preserve 
101     * ordering).
102     * 
103     * <p>Used like so:
104     * {@code for (String s : set("beep", "boop", "blorp")) { ... }}
105     * 
106     * @param elements Items that will appear within the returned {@code Set}.
107     * Cannot be {@code null}, and (for now) the items should be of the 
108     * <i>same</i> type.
109     * 
110     * @return A {@code Set} containing the items in {@code elements}. Remember
111     * that {@code Set}s only contain <i>unique</i> elements!
112     */
113    public static <E> Set<E> set(E... elements) {
114        Set<E> newSet = new LinkedHashSet<E>(elements.length);
115        Collections.addAll(newSet, elements);
116        return newSet;
117    }
118
119    /**
120     * Creates a new {@code cl} instance (limited to things implementing 
121     * {@link Collection}) populated with the {@literal "varargs"}. Useful if
122     * you truly despise {@link #arr(Object...)}, {@link #list(Object...)}, 
123     * or {@link #set(Object...)}.
124     * 
125     * <p>Example: {@code Collection<Integer> ints = collect(PriorityBlockingQueue.class, 1, 2, 3);}
126     * 
127     * @param cl A (non-abstract!) class that implements {@code Collection}. Cannot be {@code null}.
128     * @param elements Objects that will be added to the collection.
129     * 
130     * @return An instance of {@code cl} containing the given objects.
131     * 
132     * @throws RuntimeException
133     * if {@link java.lang.reflect.Constructor#newInstance(Object...) Constructor#newInstance(Object...)}
134     * had problems.
135     * 
136     * @see #arr(Object...)
137     * @see #list(Object...)
138     * @see #set(Object...)
139     */
140    @SuppressWarnings({ "unchecked", "rawtypes" }) // the only things being added to the collection are objects of type "E"
141    public static <E> Collection<E> collect(Class<? extends Collection> cl, E... elements) {
142        try {
143            Collection<E> c = cl.getConstructor().newInstance();
144            Collections.addAll(c, elements);
145            return c;
146        } catch (Exception e) {
147            throw new RuntimeException("Problem creating a new "+cl, e);
148        }
149    }
150
151    /**
152     * Determines the {@literal "length"} of a given object. This method 
153     * currently understands:<ul>
154     * <li>{@link Collection}</li>
155     * <li>{@link Map}</li> 
156     * <li>{@link CharSequence}</li>
157     * <li>{@link Array}</li>
158     * <li>{@link Iterable}</li>
159     * <li>{@link Iterator}</li>
160     * </ul>
161     * 
162     * <p>More coming!
163     * 
164     * @param o {@code Object} whose length we want. Cannot be {@code null}.
165     * 
166     * @return {@literal "Length"} of {@code o}.
167     * 
168     * @throws NullPointerException if {@code o} is {@code null}.
169     * @throws IllegalArgumentException if the method doesn't know how to test
170     * whatever type of object {@code o} might be.
171     */
172    @SuppressWarnings({"WeakerAccess"})
173    public static int len(final Object o) {
174        if (o == null) {
175            throw new NullPointerException("Null arguments do not have a length");
176        }
177        if (o instanceof Collection<?>) {
178            return ((Collection<?>)o).size();
179        }
180        else if (o instanceof Map<?, ?>) {
181            return ((Map<?, ?>)o).size();
182        }
183        else if (o instanceof CharSequence) {
184            return ((CharSequence)o).length();
185        } 
186        else if (o instanceof Iterator<?>) {
187            int count = 0;
188            Iterator<?> it = (Iterator<?>)o;
189            while (it.hasNext()) {
190                it.next();
191                count++;
192            }
193            return count;
194        }
195        else if (o instanceof Iterable<?>) {
196            return len(((Iterable<?>)o).iterator());
197        }
198
199        throw new IllegalArgumentException("Don't know how to find the length of a "+o.getClass().getName());
200    }
201
202    /**
203     * Searches an object to see if it {@literal "contains"} another object.
204     * This method currently knows how to search:<ul>
205     * <li>{@link Collection}</li>
206     * <li>{@link Map}</li> 
207     * <li>{@link CharSequence}</li>
208     * <li>{@link Array}</li>
209     * <li>{@link Iterable}</li>
210     * <li>{@link Iterator}</li>
211     * </ul>
212     * 
213     * <p>More coming!
214     * 
215     * @param collection {@code Object} that will be searched for
216     * {@code item}. Cannot be {@code null}.
217     * @param item {@code Object} to search for within {@code o}.
218     * {@code null} values are allowed.
219     * 
220     * @return {@code true} if {@code o} contains {@code item}, {@code false}
221     * otherwise.
222     * 
223     * @throws NullPointerException if {@code o} is {@code null}.
224     * @throws IllegalArgumentException if the method doesn't know how to 
225     * search whatever type of object {@code o} might be.
226     */
227    // TODO(jon:89): item should probably become an array/collection too...
228    @SuppressWarnings({"WeakerAccess"})
229    public static boolean contains(final Object collection, final Object item) {
230        if (collection == null) {
231            throw new NullPointerException("Cannot search a null object");
232        }
233        if (collection instanceof Collection<?>) {
234            return ((Collection<?>)collection).contains(item);
235        }
236        else if ((collection instanceof String) && (item instanceof CharSequence)) {
237            return ((String)collection).contains((CharSequence) item);
238        }
239        else if (collection instanceof Map<?, ?>) {
240            return ((Map<?, ?>)collection).containsKey(item);
241        }
242        else if (collection instanceof Iterator<?>) {
243            Iterator<?> it = (Iterator<?>)collection;
244            if (item == null) {
245                while (it.hasNext()) {
246                    if (it.next() == null) {
247                        return true;
248                    }
249                }
250            } else {
251                while (it.hasNext()) {
252                    if (item.equals(it.next())) {
253                        return true;
254                    }
255                }
256            }
257            return false;
258        }
259        else if (collection instanceof Iterable<?>) {
260            return contains(((Iterable<?>) collection).iterator(), item);
261        }
262        else if (collection.getClass().isArray()) {
263            for (int i = 0; i < Array.getLength(collection); i++) {
264                Object value = Array.get(collection, i);
265                if (value.equals(item)) {
266                    return true;
267                }
268            }
269        }
270        throw new IllegalArgumentException("Don't know how to search a "+collection.getClass().getName());
271    }
272
273    /**
274     * Creates an empty {@link HashSet} that uses a little cleverness with 
275     * Java's generics. Useful for eliminating redundant type information and
276     * declaring fields as {@code final}.
277     *
278     * <p>Please consider using {@link #newHashSet(int)} or
279     * {@link #newHashSet(java.util.Collection)} instead of this method.
280     *
281     * @return A new, empty {@code HashSet}.
282     *
283     * @see #newHashSet(int)
284     * @see #newHashSet(java.util.Collection)
285     */
286    @SuppressWarnings({"CollectionWithoutInitialCapacity"})
287    public static <E> Set<E> newHashSet() {
288        return new HashSet<E>();
289    }
290
291    /**
292     * Creates an empty {@link HashSet} with a given initial capacity.
293     *
294     * @param initialCapacity Initial capacity of the {@code HashSet}. Cannot
295     * be negative.
296     *
297     * @return A new, empty {@code HashSet} with the given initial capacity.
298     */
299    public static <E> Set<E> newHashSet(int initialCapacity) {
300        return new HashSet<E>(initialCapacity);
301    }
302
303    /**
304     * Copies an existing {@link Collection} into a new {@link HashSet}.
305     *
306     * @param original {@code Collection} to be copied. Cannot be {@code null}.
307     *
308     * @return A new {@code HashSet} whose contents are the same as
309     * {@code original}.
310     */
311    public static <E> Set<E> newHashSet(Collection<E> original) {
312        return new HashSet<E>(original);
313    }
314
315    /**
316     * Creates an empty {@link LinkedHashSet} that uses a little cleverness 
317     * with Java's generics. Useful for eliminating redundant type 
318     * information and declaring fields as {@code final}.
319     *
320     * <p>Please consider using {@link #newLinkedHashSet(int)} or
321     * {@link #newLinkedHashSet(java.util.Collection)} instead of this method.
322     * 
323     * @return A new, empty {@code LinkedHashSet}.
324     *
325     * @see #newLinkedHashSet(int)
326     * @see #newLinkedHashSet(java.util.Collection)
327     */
328    @SuppressWarnings({"CollectionWithoutInitialCapacity"})
329    public static <E> Set<E> newLinkedHashSet() {
330        return new LinkedHashSet<E>();
331    }
332
333    /**
334     * Creates an empty {@link LinkedHashSet} with a given initial capacity.
335     *
336     * @param initialCapacity Initial capacity of the {@code LinkedHashSet}.
337     * Cannot be negative.
338     *
339     * @return A new, empty {@code LinkedHashSet} with the given initial
340     * capacity.
341     */
342    public static <E> Set<E> newLinkedHashSet(int initialCapacity) {
343        return new LinkedHashSet<E>(initialCapacity);
344    }
345
346    /**
347     * Copies a {@link Collection} into a new {@link LinkedHashSet}.
348     * 
349     * @param original Collection to be copied. Cannot be {@code null}.
350     * 
351     * @return A new {@code LinkedHashSet} whose contents are the same as 
352     * {@code original}.
353     */
354    public static <E> Set<E> newLinkedHashSet(Collection<E> original) {
355        return new LinkedHashSet<E>(original);
356    }
357
358    /**
359     * Creates an empty {@link HashSet} that uses a little cleverness with 
360     * Java's generics. Useful for eliminating redundant type information and
361     * declaring fields as {@code final}, while also reducing compiler warnings.
362     *
363     * <p>Please consider using {@link #newMap(int)} or
364     * {@link #newMap(java.util.Map)} instead of this method.
365     *
366     * @return A new, empty {@code HashMap}.
367     *
368     * @see #newMap(int)
369     * @see #newMap(java.util.Map)
370     */
371    @SuppressWarnings({"CollectionWithoutInitialCapacity"})
372    public static <K, V> Map<K, V> newMap() {
373        return new HashMap<K, V>();
374    }
375
376    /**
377     * Creates an empty {@link HashSet} with a given initial capacity.
378     *
379     * @param initialCapacity Initial capacity of the {@code HashMap}.
380     * Cannot be negative.
381     *
382     * @return A new, empty {@code HashMap} with the given initial capacity.
383     */
384    public static <K, V> Map<K, V> newMap(int initialCapacity) {
385        return new HashMap<K, V>(initialCapacity);
386    }
387
388    /**
389     * Copies an existing {@link Map} into a new {@link HashMap}.
390     * 
391     * @param original Map to be copied. Cannot be {@code null}.
392     * 
393     * @return A new {@code HashMap} whose contents are the same as 
394     * {@code original}.
395     */
396    public static <K, V> Map<K, V> newMap(Map<K, V> original) {
397        return new HashMap<K, V>(original);
398    }
399
400    /**
401     * Creates an empty {@link LinkedHashMap} that uses a little cleverness with
402     * Java's generics. Useful for eliminating redundant type information and 
403     * declaring fields as {@code final}, while also reducing compiler warnings.
404     *
405     * <p>Please consider using {@link #newLinkedHashMap(int)} or
406     * {@link #newLinkedHashSet(java.util.Collection)} instead of this method.
407     *
408     * @return A new, empty {@code LinkedHashMap}.
409     *
410     * @see #newLinkedHashMap(int)
411     * @see #newLinkedHashMap(java.util.Map)
412     */
413    @SuppressWarnings({"CollectionWithoutInitialCapacity"})
414    public static <K, V> Map<K, V> newLinkedHashMap() {
415        return new LinkedHashMap<K, V>();
416    }
417
418    /**
419     * Creates an empty {@link LinkedHashMap} with a given initial capacity.
420     *
421     * @param initialCapacity Initial capacity of the {@code LinkedHashMap}.
422     * Cannot be negative.
423     *
424     * @return A new, empty {@code LinkedHashMap} with the given initial
425     * capacity.
426     */
427    public static <K, V> Map<K, V> newLinkedHashMap(int initialCapacity) {
428        return new LinkedHashMap<K, V>(initialCapacity);
429    }
430
431    /**
432     * Copies an existing {@link Map} into a new {@link LinkedHashMap}.
433     * 
434     * @param original Map to be copied. Cannot be {@code null}.
435     * 
436     * @return A new {@code LinkedHashMap} whose contents are the same as 
437     * {@code original}.
438     */
439    public static <K, V> Map<K, V> newLinkedHashMap(Map<K, V> original) {
440        return new LinkedHashMap<K, V>(original);
441    }
442
443    /**
444     * Abuses Java's sad {@literal "type"} implementation to create a new
445     * {@link ConcurrentHashMap}.
446     * 
447     * @return Shiny and new {@code ConcurrentHashMap}
448     */
449    public static <K, V> Map<K, V> concurrentMap() {
450        return new ConcurrentHashMap<K,V>();
451    }
452
453    /**
454     * Creates an empty {@link CopyOnWriteArrayList}. Keep in mind that you 
455     * only want to use {@code CopyOnWriteArrayList} for lists that are not 
456     * going to be modified very often!
457     * 
458     * @return A new, empty {@code CopyOnWriteArrayList}.
459     */
460    public static <E> List<E> concurrentList() {
461        return new CopyOnWriteArrayList<E>();
462    }
463
464    /**
465     * Creates a new {@link CopyOnWriteArrayList} that contains all of the 
466     * elements in {@code original}. Keep in mind that you only want to use 
467     * {@code CopyOnWriteArrayList} for lists that are not going to be 
468     * modified very often!
469     * 
470     * @param original Collection to be copied into the new list.
471     * 
472     * @return A new {@code CopyOnWriteArrayList} whose contents are the same 
473     * as {@code original}.
474     */
475    public static <E> List<E> concurrentList(Collection<E> original) {
476        return new CopyOnWriteArrayList<E>(original);
477    }
478
479    /**
480     * Creates a new {@link CopyOnWriteArrayList} from the incoming 
481     * {@literal "varargs"}. Keep in mind that you only want to use 
482     * {@code CopyOnWriteArrayList} for lists that are not going to be modified 
483     * very often!
484     * 
485     * @param elems Elements that will be contained in the resulting list.
486     * 
487     * @return A new {@code CopyOnWriteArrayList} that contains the incoming 
488     * objects.
489     */
490    public static <E> List<E> concurrentList(E... elems) {
491        return new CopyOnWriteArrayList<E>(elems);
492    }
493
494    /**
495     * Creates a new {@link CopyOnWriteArraySet}. Keep in mind that you only 
496     * want to use a {@code CopyOnWriteArraySet} for sets that are not going to
497     * be modified very often!
498     * 
499     * @return A new, empty {@code CopyOnWriteArraySet}.
500     */
501    public static <E> Set<E> concurrentSet() {
502        return new CopyOnWriteArraySet<E>();
503    }
504
505    /**
506     * Creates a new {@link CopyOnWriteArraySet} that contains all of the 
507     * elements in {@code original}. Keep in mind that you only want to use a 
508     * {@code CopyOnWriteArraySet} for sets that are not going to be modified 
509     * very often!
510     * 
511     * @param original Collection to be copied into the new set.
512     * 
513     * @return A new {@code CopyOnWriteArraySet} whose contents are the same as
514     * {@code original}.
515     */
516    public static <E> Set<E> concurrentSet(Collection<E> original) {
517        return new CopyOnWriteArraySet<E>(original);
518    }
519
520    /**
521     * Creates a new {@link CopyOnWriteArraySet} from the incoming 
522     * {@literal "varargs"}. Keep in mind that you only want to use a 
523     * {@code CopyOnWriteArraySet} for sets that are not going to be modified 
524     * very often!
525     * 
526     * @param elems Elements that will be contained in the resulting set.
527     * 
528     * @return A new {@code CopyOnWriteArraySet} that contains the incoming 
529     * objects.
530     */
531    public static <E> Set<E> concurrentSet(E... elems) {
532        Set<E> set = new CopyOnWriteArraySet<E>();
533        Collections.addAll(set, elems);
534        return set;
535    }
536
537    /**
538     * Creates an empty {@link ArrayList} that uses a little cleverness with
539     * Java's generics. Useful for eliminating redundant type information and
540     * declaring fields as {@code final}.
541     * 
542     * <p>Used like so:
543     * {@code List<String> listy = arrList();}
544     *
545     * <p>Please consider using {@link #arrList(int)} or
546     * {@link #arrList(java.util.Collection)} instead of this method.
547     * 
548     * @return A new, empty {@code ArrayList}.
549     *
550     * @see #arrList(int)
551     * @see #arrList(java.util.Collection)
552     */
553    @SuppressWarnings({"CollectionWithoutInitialCapacity"})
554    public static <E> List<E> arrList() {
555        return new ArrayList<E>();
556    }
557
558    /**
559     * Creates an empty {@link ArrayList} with a given capacity.
560     * 
561     * @param capacity The initial size of the returned {@code ArrayList}.
562     * 
563     * @return A new, empty {@code ArrayList} that has an initial capacity of
564     * {@code capacity} elements.
565     * 
566     * @see ArrayList#ArrayList(int)
567     */
568    public static <E> List<E> arrList(final int capacity) {
569        return new ArrayList<E>(capacity);
570    }
571
572    /**
573     * Copies an existing {@link Collection} into a new {@link ArrayList}.
574     * 
575     * @param c {@code Collection} whose elements are to be placed into the 
576     * returned {@code ArrayList}.
577     * 
578     * @return An {@code ArrayList} containing the elements of {@code c}.
579     * 
580     * @see ArrayList#ArrayList(Collection)
581     */
582    public static <E> List<E> arrList(final Collection<? extends E> c) {
583        return new ArrayList<E>(c);
584    }
585
586    /**
587     * Copies an existing {@link Collection} into a new (non-abstract!) 
588     * {@code Collection} class.
589     * 
590     * @param cl Non-abstract {@code Collection} class.
591     * @param old An existing {@code Collection}.
592     * 
593     * @return A new instance of {@code cl} that contains all of the elements
594     * from {@code old}.
595     * 
596     * @throws RuntimeException if there was trouble creating a new instance 
597     * of {@code cl}.
598     * 
599     * @see #collect(Class, Object...)
600     */
601    // not sure about the utility of this one...
602    @SuppressWarnings({ "unchecked", "rawtypes" }) // again, only adding items of type "E"
603    public static <E> Collection<E> recollect(Class<? extends Collection> cl, Collection<E> old) {
604        try {
605            Collection<E> c = cl.getConstructor().newInstance();
606            c.addAll(old);
607            return c;
608        } catch (Exception e) {
609            throw new RuntimeException("", e);
610        }
611    }
612
613    /**
614     * Takes arrays of {@code keys} and {@code values} and merges them 
615     * together to form a {@link Map}. The returned {@code Map} is a 
616     * {@link LinkedHashMap} and is truncated in length to the length of the 
617     * shorter parameter.
618     * 
619     * <p>This is intended for use as {@literal "varargs"} supplied to 
620     * {@link #arr(Object...)}. Rather than doing something ugly like:
621     * <pre>
622     * Map&lt;String, String&gt; mappy = new LinkedHashMap&lt;String, String&gt;();
623     * mappy.put("key0", "val0");
624     * mappy.put("key1", "val1");
625     * ...
626     * mappy.put("keyN", "valN");
627     * </pre>
628     * 
629     * Simply do like so:
630     * <pre>
631     * mappy = zipMap(
632     *     arr("key0", "key1", ..., "keyN"),
633     *     arr("val0", "val1", ..., "valN"));
634     * </pre>
635     * 
636     * <p>The latter approach also allows you to make {@code static final} 
637     * {@link Map}s much more easily.
638     * 
639     * @param keys Array whose elements will be the keys in a {@code Map}.
640     * @param values Array whose elements will the values in a {@code Map}.
641     * 
642     * @return A {@code Map} whose entries are of the form 
643     * {@code keys[N], values[N]}.
644     * 
645     * @see #arr(Object...)
646     * @see #zipMap(java.util.Collection, java.util.Collection)
647     */
648    public static <K, V> Map<K, V> zipMap(K[] keys, V[] values) {
649        Map<K, V> zipped = new LinkedHashMap<K, V>(keys.length);
650        for (int i = 0; (i < keys.length && i < values.length); i++) {
651            zipped.put(keys[i], values[i]);
652        }
653        return zipped;
654    }
655
656    /**
657     * A version of {@link #zipMap(Object[], Object[])} that works with 
658     * {@link Collection}s.
659     * 
660     * @param keys Items that will be the keys in the resulting {@code Map}.
661     * @param values Items that will be the values in the result {@code Map}.
662     * 
663     * @return A {@code Map} whose entries are of the form 
664     * {@code keys[N], values[N]}.
665     * 
666     * @see #zipMap(Object[], Object[])
667     */
668    public static <K, V> Map<K, V> zipMap(Collection<? extends K> keys, Collection<? extends V> values) {
669        Map<K, V> zipped = new LinkedHashMap<K, V>(keys.size());
670        Iterator<? extends K> keyIterator = keys.iterator();
671        Iterator<? extends V> valueIterator = values.iterator();
672        while (keyIterator.hasNext() && valueIterator.hasNext()) {
673            zipped.put(keyIterator.next(), valueIterator.next());
674        }
675        return zipped;
676    }
677
678    /**
679     * Applies a given function to each item in a given list.
680     * 
681     * @param f The {@link Function} to apply.
682     * @param as The list whose items are to be fed into {@code f}.
683     * 
684     * @return New list containing the results of each element of {@code as}
685     * being passed through {@code f}.
686     */
687    public static <A, B> List<B> map(final Function<A, B> f, List<A> as) {
688        List<B> bs = arrList(as.size());
689        for (A a : as) {
690            bs.add(f.apply(a));
691        }
692        return bs;
693    }
694
695    /**
696     * Applies a given function to each item in a given {@link Set}.
697     * 
698     * @param f The {@link Function} to apply to {@code as}.
699     * @param as The {@code Set} whose items are to be fed into {@code f}.
700     * 
701     * @return New {@code Set} containing the results of passing each element
702     * in {@code as} through {@code f}.
703     */
704    public static <A, B> Set<B> map(final Function<A, B> f, Set<A> as) {
705        Set<B> bs = newLinkedHashSet(as.size());
706        for (A a : as) {
707            bs.add(f.apply(a));
708        }
709        return bs;
710    }
711
712    /**
713     * {@literal "Generics-friendly"} way to cast an object of some superclass 
714     * ({@code A}) to a subclass or implementation ({@code B}). This method will
715     * fail if you attempt to cast to a type that is not a subclass of type 
716     * {@code A}.
717     * 
718     * <p>Example/Justification:<br/>
719     * Consider a method like {@link ucar.unidata.xml.XmlUtil#findChildren(org.w3c.dom.Node, String) XmlUtil.findChildren(Node, String)}.<br/>
720     * Despite {@code findChildren} only returning lists containing {@code Node} 
721     * objects, Java will generate a warning for the following code:
722     * <pre>
723     * import ucar.unidata.xml.XmlUtil;
724     * ....
725     * List<Node> nodes = XmlUtil.findChildren(panel, "blah");
726     * </pre>
727     * {@code cast} is a nice and terse way to avoid those warnings. Here's the 
728     * previous example (with static imports of {@code cast} and {@code findChildren}):
729     * <pre>
730     * import static ucar.unidata.xml.XmlUtil.findChildren;
731     * import static edu.wisc.ssec.mcidasv.util.CollectionHelpers.cast;
732     * ....
733     * List<Node> nodes = cast(findChildren(panel, "blah"));
734     * </pre>
735     * 
736     * @param <A> Superclass of {@code B}. This is what you are 
737     * {@literal "casting from"}...likely {@code Object} in most cases
738     * @param <B> Subclass of {@code A}. This is what you are 
739     * {@literal "casting to"}.
740     * 
741     * @param o The object whose type you are casting.
742     * 
743     * @return {@code o}, casted from type {@code A} to {@code B}. Enjoy!
744     */
745    @SuppressWarnings("unchecked") public static <A, B extends A> B cast(A o) {
746        return (B)o;
747    }
748}