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 }