001    /**
002     * Copyright 2010-2013 The Kuali Foundation
003     *
004     * Licensed under the Educational Community License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.opensource.org/licenses/ecl2.php
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.kuali.common.util;
017    
018    import java.io.File;
019    import java.io.IOException;
020    import java.io.StringReader;
021    import java.util.ArrayList;
022    import java.util.Arrays;
023    import java.util.Collection;
024    import java.util.Collections;
025    import java.util.HashMap;
026    import java.util.List;
027    import java.util.Map;
028    import java.util.Set;
029    import java.util.TreeSet;
030    
031    import org.apache.commons.io.IOUtils;
032    import org.apache.commons.lang3.StringUtils;
033    
034    public class CollectionUtils {
035    
036            /**
037             * Null safe method for converting an array of objects into a list. Never returns null.
038             */
039            public static List<Object> asList(Object... objects) {
040                    List<Object> list = new ArrayList<Object>();
041                    if (objects == null) {
042                            return list;
043                    }
044                    for (Object element : objects) {
045                            if (element != null) {
046                                    list.add(element);
047                            }
048                    }
049                    return list;
050            }
051    
052            /**
053             * Null safe method for converting an untyped array of classes into a list. Never returns null.
054             */
055            public static List<Class<?>> asList(Class<?>... classes) {
056                    List<Class<?>> list = new ArrayList<Class<?>>();
057                    if (classes == null) {
058                            return list;
059                    }
060                    for (Class<?> element : classes) {
061                            if (element != null) {
062                                    list.add(element);
063                            }
064                    }
065                    return list;
066            }
067    
068            /**
069             * Return an array of int's that represents as even of a split as possible
070             * 
071             * For example: passing in 100,7 returns 15, 15, 14, 14, 14, 14, 14
072             * 
073             * @param numerator
074             * @param denominator
075             * @return
076             */
077            public static int[] getDivideEvenly(int number, int howManyWays) {
078                    Assert.isTrue(howManyWays > 0, "howManyWays must be a positive integer");
079                    int quotient = number / howManyWays;
080                    int remainder = number % howManyWays;
081    
082                    int[] lengths = new int[howManyWays];
083                    for (int i = 0; i < howManyWays; i++) {
084                            int length = i < remainder ? quotient + 1 : quotient;
085                            lengths[i] = length;
086                    }
087                    return lengths;
088            }
089    
090            /**
091             * Split <code>elements</code> evenly into separate lists divided up <code>howManyWays</code>
092             */
093            public static final <T> List<List<T>> splitEvenly(List<T> elements, int howManyWays) {
094                    // Can't split 2 things 3 ways
095                    if (howManyWays > elements.size()) {
096                            howManyWays = elements.size();
097                    }
098                    int[] lengths = getDivideEvenly(elements.size(), howManyWays);
099                    int offset = 0;
100                    List<List<T>> listOfLists = new ArrayList<List<T>>();
101                    for (int i = 0; i < lengths.length; i++) {
102                            int length = lengths[i];
103                            List<T> sublist = new ArrayList<T>();
104                            for (int j = offset; j < offset + length; j++) {
105                                    sublist.add(elements.get(j));
106                            }
107                            listOfLists.add(sublist);
108                            offset += length;
109                    }
110                    return listOfLists;
111            }
112    
113            /**
114             * Prefix the strings passed in with their position in the list (left padded with zero's). The padding is the number of digits in the size of the list. A list with 100 elements
115             * will return strings prefixed with 000, 001, etc.
116             */
117            public static final List<String> getSequencedStrings(List<String> strings, int initialSequenceNumber) {
118                    List<String> sequencedStrings = new ArrayList<String>();
119                    int size = strings.size();
120                    int length = new Integer(size).toString().length();
121                    String prefix = StringUtils.repeat("0", length);
122                    for (String string : strings) {
123                            String sequence = StringUtils.right(prefix + (initialSequenceNumber++), length);
124                            String sequencedString = sequence + "-" + string;
125                            sequencedStrings.add(sequencedString);
126                    }
127                    return sequencedStrings;
128            }
129    
130            /**
131             * Prefix the strings passed in with their position in the list (left padded with zero's). The padding is the number of digits in the size of the list. A list with 100 elements
132             * will return strings prefixed with 000, 001, etc.
133             */
134            public static final List<String> getSequencedStrings(List<String> strings) {
135                    return getSequencedStrings(strings, 0);
136            }
137    
138            /**
139             * Return a new <code>List</code> containing the unique set of strings from <code>strings</code>
140             */
141            public static final List<String> getUniqueStrings(List<String> strings) {
142                    List<String> unique = new ArrayList<String>();
143                    for (String string : strings) {
144                            if (!unique.contains(string)) {
145                                    unique.add(string);
146                            }
147                    }
148                    return unique;
149            }
150    
151            public static final List<File> getUniqueFiles(List<File> files) {
152                    List<String> strings = new ArrayList<String>();
153                    for (File file : files) {
154                            strings.add(LocationUtils.getCanonicalPath(file));
155                    }
156                    List<String> uniqueStrings = getUniqueStrings(strings);
157                    List<File> uniqueFiles = new ArrayList<File>();
158                    for (String uniqueString : uniqueStrings) {
159                            uniqueFiles.add(new File(uniqueString));
160                    }
161                    return uniqueFiles;
162            }
163    
164            public static final List<String> getLines(String s) {
165                    if (s == null) {
166                            return Collections.<String> emptyList();
167                    }
168                    try {
169                            return IOUtils.readLines(new StringReader(s));
170                    } catch (IOException e) {
171                            throw new IllegalStateException(e);
172                    }
173            }
174    
175            /**
176             * Return a new list containing the unique set of strings contained in both lists
177             */
178            public static final List<String> combineStringsUniquely(List<String> list1, List<String> list2) {
179                    List<String> newList = getUniqueStrings(list1);
180                    for (String element : list2) {
181                            if (!newList.contains(element)) {
182                                    newList.add(element);
183                            }
184                    }
185                    return newList;
186            }
187    
188            protected static final <T> T getNewInstance(Class<T> c) {
189                    try {
190                            return c.newInstance();
191                    } catch (IllegalAccessException e) {
192                            throw new IllegalArgumentException(e);
193                    } catch (InstantiationException e) {
194                            throw new IllegalArgumentException(e);
195                    }
196            }
197    
198            /**
199             * Create a new list containing new instances of <code>c</code>
200             */
201            public static final <T> List<T> getNewList(Class<T> c, int size) {
202                    List<T> list = new ArrayList<T>();
203                    for (int i = 0; i < size; i++) {
204                            T element = getNewInstance(c);
205                            list.add(element);
206                    }
207                    return list;
208            }
209    
210            /**
211             * Return a list containing only the elements where the corresponding index in the <code>includes</code> list is <code>true</code>. <code>includes</code> and <code>list</code>
212             * must be the same size.
213             */
214            public static final <T> List<T> getList(List<Boolean> includes, List<T> list) {
215                    Assert.isTrue(includes.size() == list.size());
216                    List<T> included = new ArrayList<T>();
217                    for (int i = 0; i < includes.size(); i++) {
218                            if (includes.get(i)) {
219                                    included.add(list.get(i));
220                            }
221                    }
222                    return included;
223            }
224    
225            /**
226             * Combine the list of lists into a single list
227             */
228            public static final <T> List<T> combineLists(List<List<T>> listOfLists) {
229                    List<T> combined = new ArrayList<T>();
230                    for (List<T> list : listOfLists) {
231                            combined.addAll(list);
232                    }
233                    return combined;
234            }
235    
236            /**
237             * Combine the list of maps into a single map
238             */
239            public static final <K, V> Map<K, V> combineMaps(List<Map<K, V>> listOfMaps) {
240                    Map<K, V> combined = new HashMap<K, V>();
241                    for (Map<K, V> map : listOfMaps) {
242                            combined.putAll(map);
243                    }
244                    return combined;
245            }
246    
247            /**
248             * Return a combined list where <code>required</code> is always the first element in the list
249             */
250            public static final <T> List<T> combine(T element, List<T> list) {
251                    Assert.notNull(element, "element is required");
252                    if (list == null) {
253                            return Collections.singletonList(element);
254                    } else {
255                            List<T> combined = new ArrayList<T>();
256                            // Always insert required as the first element in the list
257                            combined.add(element);
258                            // Add the other elements
259                            for (T optional : list) {
260                                    combined.add(optional);
261                            }
262                            return combined;
263                    }
264            }
265    
266            /**
267             * If <code>o==null</code> return an empty list otherwise return a singleton list.
268             */
269            public static final <T> List<T> toEmptyList(T o) {
270                    if (o == null) {
271                            return Collections.<T> emptyList();
272                    } else {
273                            return Collections.singletonList(o);
274                    }
275            }
276    
277            /**
278             * If <code>list==null</code> return an empty list otherwise return <code>list</code>
279             */
280            public static final <T> List<T> toEmptyList(List<T> list) {
281                    if (list == null) {
282                            return Collections.<T> emptyList();
283                    } else {
284                            return list;
285                    }
286            }
287    
288            public static final <T> List<T> toNullIfEmpty(List<T> list) {
289                    if (isEmpty(list)) {
290                            return null;
291                    } else {
292                            return list;
293                    }
294            }
295    
296            public static final <T> Collection<T> toNullIfEmpty(Collection<T> c) {
297                    if (isEmpty(c)) {
298                            return null;
299                    } else {
300                            return c;
301                    }
302            }
303    
304            public static final <T> List<T> getPreFilledList(int size, T value) {
305                    if (value == null || size < 1) {
306                            return Collections.<T> emptyList();
307                    } else {
308                            List<T> list = new ArrayList<T>(size);
309                            for (int i = 0; i < size; i++) {
310                                    list.add(value);
311                            }
312                            return list;
313                    }
314            }
315    
316            public static final String getCSV(List<String> strings) {
317                    StringBuilder sb = new StringBuilder();
318                    for (int i = 0; i < toEmptyList(strings).size(); i++) {
319                            if (i != 0) {
320                                    sb.append(",");
321                            }
322                            sb.append(strings.get(i));
323                    }
324                    return sb.toString();
325            }
326    
327            public static final String getSpaceSeparatedString(List<?> list) {
328                    list = toEmptyList(list);
329                    StringBuilder sb = new StringBuilder();
330                    for (int i = 0; i < list.size(); i++) {
331                            if (i != 0) {
332                                    sb.append(" ");
333                            }
334                            sb.append(list.get(i).toString());
335                    }
336                    return sb.toString();
337            }
338    
339            public static final Object[] toObjectArray(List<Object> objects) {
340                    return objects.toArray(new Object[objects.size()]);
341            }
342    
343            public static final String[] toStringArray(List<String> strings) {
344                    return strings.toArray(new String[strings.size()]);
345            }
346    
347            public static final boolean isEmpty(Collection<?> c) {
348                    return c == null || c.size() == 0;
349            }
350    
351            public static final List<String> sortedMerge(List<String> list, String csv) {
352                    Set<String> set = new TreeSet<String>();
353                    set.addAll(toEmptyList(list));
354                    set.addAll(getTrimmedListFromCSV(csv));
355                    return new ArrayList<String>(set);
356            }
357    
358            public static final List<String> getTrimmedListFromCSV(String csv) {
359                    if (StringUtils.isBlank(csv)) {
360                            return Collections.<String> emptyList();
361                    }
362                    List<String> list = new ArrayList<String>();
363                    String[] tokens = Str.splitAndTrimCSV(csv);
364                    list.addAll(Arrays.asList(tokens));
365                    return list;
366            }
367    
368            public static final List<String> combineStrings(List<String> list1, List<String> list2, List<String> list3) {
369                    List<String> combined = new ArrayList<String>();
370                    nullSafeAdd(combined, list1);
371                    nullSafeAdd(combined, list2);
372                    nullSafeAdd(combined, list3);
373                    return combined;
374            }
375    
376            /**
377             * Return a new list containing all of the strings from both lists with string added in between the strings from both lists
378             */
379            public static final List<String> combineStrings(List<String> list1, String string, List<String> list2) {
380                    return combineStrings(list1, toEmptyList(string), list2);
381            }
382    
383            /**
384             * Return a new list containing all of the strings from both lists
385             */
386            public static final List<String> combineStrings(List<String> list1, List<String> list2) {
387                    return combineStrings(list1, (String) null, list2);
388            }
389    
390            public static final <T> void nullSafeAdd(List<T> list1, List<T> list2) {
391                    if (list2 != null) {
392                            list1.addAll(list2);
393                    }
394            }
395    
396            /**
397             * Return <code>true</code> if <code>s</code> contains any of the strings from <code>strings</code>
398             */
399            public static final boolean containsAny(String s, List<String> strings) {
400                    for (String string : strings) {
401                            if (StringUtils.contains(s, string)) {
402                                    return true;
403                            }
404                    }
405                    return false;
406            }
407    }