001    /**
002     * Copyright 2010-2012 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             * Return an array of int's that represents as even of a split as possible
038             * 
039             * For example: passing in 100,7 returns 15, 15, 14, 14, 14, 14, 14
040             * 
041             * @param numerator
042             * @param denominator
043             * @return
044             */
045            public static int[] getDivideEvenly(int number, int howManyWays) {
046                    Assert.isTrue(howManyWays > 0, "howManyWays must be a positive integer");
047                    int quotient = number / howManyWays;
048                    int remainder = number % howManyWays;
049    
050                    int[] lengths = new int[howManyWays];
051                    for (int i = 0; i < howManyWays; i++) {
052                            int length = i < remainder ? quotient + 1 : quotient;
053                            lengths[i] = length;
054                    }
055                    return lengths;
056            }
057    
058            /**
059             * Split <code>elements</code> evenly into separate lists divided up <code>howManyWays</code>
060             */
061            public static final <T> List<List<T>> splitEvenly(List<T> elements, int howManyWays) {
062                    // Can't split 2 things 3 ways
063                    if (howManyWays > elements.size()) {
064                            howManyWays = elements.size();
065                    }
066                    int[] lengths = getDivideEvenly(elements.size(), howManyWays);
067                    int offset = 0;
068                    List<List<T>> listOfLists = new ArrayList<List<T>>();
069                    for (int i = 0; i < lengths.length; i++) {
070                            int length = lengths[i];
071                            List<T> sublist = new ArrayList<T>();
072                            for (int j = offset; j < offset + length; j++) {
073                                    sublist.add(elements.get(j));
074                            }
075                            listOfLists.add(sublist);
076                            offset += length;
077                    }
078                    return listOfLists;
079            }
080    
081            /**
082             * 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
083             * will return strings prefixed with 000, 001, etc.
084             */
085            public static final List<String> getSequencedStrings(List<String> strings, int initialSequenceNumber) {
086                    List<String> sequencedStrings = new ArrayList<String>();
087                    int size = strings.size();
088                    int length = new Integer(size).toString().length();
089                    String prefix = StringUtils.repeat("0", length);
090                    for (String string : strings) {
091                            String sequence = StringUtils.right(prefix + (initialSequenceNumber++), length);
092                            String sequencedString = sequence + "-" + string;
093                            sequencedStrings.add(sequencedString);
094                    }
095                    return sequencedStrings;
096            }
097    
098            /**
099             * 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
100             * will return strings prefixed with 000, 001, etc.
101             */
102            public static final List<String> getSequencedStrings(List<String> strings) {
103                    return getSequencedStrings(strings, 0);
104            }
105    
106            /**
107             * Return a new <code>List</code> containing the unique set of strings from <code>strings</code>
108             */
109            public static final List<String> getUniqueStrings(List<String> strings) {
110                    List<String> unique = new ArrayList<String>();
111                    for (String string : strings) {
112                            if (!unique.contains(string)) {
113                                    unique.add(string);
114                            }
115                    }
116                    return unique;
117            }
118    
119            public static final List<File> getUniqueFiles(List<File> files) {
120                    List<String> strings = new ArrayList<String>();
121                    for (File file : files) {
122                            strings.add(LocationUtils.getCanonicalPath(file));
123                    }
124                    List<String> uniqueStrings = getUniqueStrings(strings);
125                    List<File> uniqueFiles = new ArrayList<File>();
126                    for (String uniqueString : uniqueStrings) {
127                            uniqueFiles.add(new File(uniqueString));
128                    }
129                    return uniqueFiles;
130            }
131    
132            public static final List<String> getLines(String s) {
133                    if (s == null) {
134                            return Collections.<String> emptyList();
135                    }
136                    try {
137                            return IOUtils.readLines(new StringReader(s));
138                    } catch (IOException e) {
139                            throw new IllegalStateException(e);
140                    }
141            }
142    
143            /**
144             * Return a new list containing the unique set of strings contained in both lists
145             */
146            public static final List<String> combineStringsUniquely(List<String> list1, List<String> list2) {
147                    List<String> newList = getUniqueStrings(list1);
148                    for (String element : list2) {
149                            if (!newList.contains(element)) {
150                                    newList.add(element);
151                            }
152                    }
153                    return newList;
154            }
155    
156            protected static final <T> T getNewInstance(Class<T> c) {
157                    try {
158                            return c.newInstance();
159                    } catch (IllegalAccessException e) {
160                            throw new IllegalArgumentException(e);
161                    } catch (InstantiationException e) {
162                            throw new IllegalArgumentException(e);
163                    }
164            }
165    
166            /**
167             * Create a new list containing new instances of <code>c</code>
168             */
169            public static final <T> List<T> getNewList(Class<T> c, int size) {
170                    List<T> list = new ArrayList<T>();
171                    for (int i = 0; i < size; i++) {
172                            T element = getNewInstance(c);
173                            list.add(element);
174                    }
175                    return list;
176            }
177    
178            /**
179             * 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>
180             * must be the same size.
181             */
182            public static final <T> List<T> getList(List<Boolean> includes, List<T> list) {
183                    Assert.isTrue(includes.size() == list.size());
184                    List<T> included = new ArrayList<T>();
185                    for (int i = 0; i < includes.size(); i++) {
186                            if (includes.get(i)) {
187                                    included.add(list.get(i));
188                            }
189                    }
190                    return included;
191            }
192    
193            /**
194             * Combine the list of lists into a single list
195             */
196            public static final <T> List<T> combineLists(List<List<T>> listOfLists) {
197                    List<T> combined = new ArrayList<T>();
198                    for (List<T> list : listOfLists) {
199                            combined.addAll(list);
200                    }
201                    return combined;
202            }
203    
204            /**
205             * Combine the list of maps into a single map
206             */
207            public static final <K, V> Map<K, V> combineMaps(List<Map<K, V>> listOfMaps) {
208                    Map<K, V> combined = new HashMap<K, V>();
209                    for (Map<K, V> map : listOfMaps) {
210                            combined.putAll(map);
211                    }
212                    return combined;
213            }
214    
215            /**
216             * Return a combined list where <code>required</code> is always the first element in the list
217             */
218            public static final <T> List<T> combine(T element, List<T> list) {
219                    Assert.notNull(element, "element is required");
220                    if (list == null) {
221                            return Collections.singletonList(element);
222                    } else {
223                            List<T> combined = new ArrayList<T>();
224                            // Always insert required as the first element in the list
225                            combined.add(element);
226                            // Add the other elements
227                            for (T optional : list) {
228                                    combined.add(optional);
229                            }
230                            return combined;
231                    }
232            }
233    
234            /**
235             * If <code>o==null</code> return an empty list otherwise return a singleton list.
236             */
237            public static final <T> List<T> toEmptyList(T o) {
238                    if (o == null) {
239                            return Collections.<T> emptyList();
240                    } else {
241                            return Collections.singletonList(o);
242                    }
243            }
244    
245            /**
246             * If <code>list==null</code> return an empty list otherwise return <code>list</code>
247             */
248            public static final <T> List<T> toEmptyList(List<T> list) {
249                    if (list == null) {
250                            return Collections.<T> emptyList();
251                    } else {
252                            return list;
253                    }
254            }
255    
256            public static final <T> List<T> toNullIfEmpty(List<T> list) {
257                    if (isEmpty(list)) {
258                            return null;
259                    } else {
260                            return list;
261                    }
262            }
263    
264            public static final <T> Collection<T> toNullIfEmpty(Collection<T> c) {
265                    if (isEmpty(c)) {
266                            return null;
267                    } else {
268                            return c;
269                    }
270            }
271    
272            public static final <T> List<T> getPreFilledList(int size, T value) {
273                    if (value == null || size < 1) {
274                            return Collections.<T> emptyList();
275                    } else {
276                            List<T> list = new ArrayList<T>(size);
277                            for (int i = 0; i < size; i++) {
278                                    list.add(value);
279                            }
280                            return list;
281                    }
282            }
283    
284            public static final String getCSV(List<String> strings) {
285                    StringBuilder sb = new StringBuilder();
286                    for (int i = 0; i < toEmptyList(strings).size(); i++) {
287                            if (i != 0) {
288                                    sb.append(",");
289                            }
290                            sb.append(strings.get(i));
291                    }
292                    return sb.toString();
293            }
294    
295            public static final String getSpaceSeparatedString(List<?> list) {
296                    list = toEmptyList(list);
297                    StringBuilder sb = new StringBuilder();
298                    for (int i = 0; i < list.size(); i++) {
299                            if (i != 0) {
300                                    sb.append(" ");
301                            }
302                            sb.append(list.get(i).toString());
303                    }
304                    return sb.toString();
305            }
306    
307            public static final Object[] toObjectArray(List<Object> objects) {
308                    return objects.toArray(new Object[objects.size()]);
309            }
310    
311            public static final String[] toStringArray(List<String> strings) {
312                    return strings.toArray(new String[strings.size()]);
313            }
314    
315            public static final boolean isEmpty(Collection<?> c) {
316                    return c == null || c.size() == 0;
317            }
318    
319            public static final List<String> sortedMerge(List<String> list, String csv) {
320                    Set<String> set = new TreeSet<String>();
321                    set.addAll(toEmptyList(list));
322                    set.addAll(getTrimmedListFromCSV(csv));
323                    return new ArrayList<String>(set);
324            }
325    
326            public static final List<String> getTrimmedListFromCSV(String csv) {
327                    if (StringUtils.isBlank(csv)) {
328                            return Collections.<String> emptyList();
329                    }
330                    List<String> list = new ArrayList<String>();
331                    String[] tokens = Str.splitAndTrimCSV(csv);
332                    list.addAll(Arrays.asList(tokens));
333                    return list;
334            }
335    
336            public static final List<String> combineStrings(List<String> list1, List<String> list2, List<String> list3) {
337                    List<String> combined = new ArrayList<String>();
338                    nullSafeAdd(combined, list1);
339                    nullSafeAdd(combined, list2);
340                    nullSafeAdd(combined, list3);
341                    return combined;
342            }
343    
344            /**
345             * Return a new list containing all of the strings from both lists with string added in between the strings from both lists
346             */
347            public static final List<String> combineStrings(List<String> list1, String string, List<String> list2) {
348                    return combineStrings(list1, toEmptyList(string), list2);
349            }
350    
351            /**
352             * Return a new list containing all of the strings from both lists
353             */
354            public static final List<String> combineStrings(List<String> list1, List<String> list2) {
355                    return combineStrings(list1, (String) null, list2);
356            }
357    
358            public static final <T> void nullSafeAdd(List<T> list1, List<T> list2) {
359                    if (list2 != null) {
360                            list1.addAll(list2);
361                    }
362            }
363    
364            /**
365             * Return <code>true</code> if <code>s</code> contains any of the strings from <code>strings</code>
366             */
367            public static final boolean containsAny(String s, List<String> strings) {
368                    for (String string : strings) {
369                            if (StringUtils.contains(s, string)) {
370                                    return true;
371                            }
372                    }
373                    return false;
374            }
375    }