1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.collections4;
18
19 import java.util.Arrays;
20 import java.util.Collection;
21 import java.util.Comparator;
22 import java.util.Enumeration;
23 import java.util.Iterator;
24 import java.util.List;
25 import java.util.Objects;
26
27 import org.apache.commons.collections4.iterators.SingletonIterator;
28
29 /**
30 * A FluentIterable provides a powerful yet simple API for manipulating
31 * Iterable instances in a fluent manner.
32 * <p>
33 * A FluentIterable can be created either from an Iterable or from a set
34 * of elements. The following types of methods are provided:
35 * </p>
36 * <ul>
37 * <li>fluent methods which return a new {@code FluentIterable} instance,
38 * providing a view of the original iterable (for example filter(Predicate));
39 * <li>conversion methods which copy the FluentIterable's contents into a
40 * new collection or array (for example toList());
41 * <li>utility methods which answer questions about the FluentIterable's
42 * contents (for example size(), anyMatch(Predicate)).
43 * <li>
44 * </ul>
45 * <p>
46 * The following example outputs the first 3 even numbers in the range [1, 10]
47 * into a list:
48 * </p>
49 * <pre>
50 * List<String> result =
51 * FluentIterable
52 * .of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
53 * .filter(new Predicate<Integer>() {
54 * public boolean evaluate(Integer number) {
55 * return number % 2 == 0;
56 * }
57 * )
58 * .transform(TransformerUtils.stringValueTransformer())
59 * .limit(3)
60 * .toList();
61 * </pre>
62 * The resulting list will contain the following elements:
63 * <pre>[2, 4, 6]</pre>
64 *
65 * @param <E> the element type
66 * @since 4.1
67 */
68 public class FluentIterable<E> implements Iterable<E> {
69
70 /**
71 * Creates a new empty FluentIterable.
72 *
73 * @param <T> the element type
74 * @return a new empty FluentIterable
75 */
76 public static <T> FluentIterable<T> empty() {
77 return IterableUtils.EMPTY_ITERABLE;
78 }
79
80 /**
81 * Constructs a new FluentIterable from the provided iterable. If the
82 * iterable is already an instance of FluentIterable, the instance
83 * will be returned instead.
84 * <p>
85 * The returned iterable's iterator supports {@code remove()} when the
86 * corresponding input iterator supports it.
87 * </p>
88 *
89 * @param <T> the element type
90 * @param iterable the iterable to wrap into a FluentIterable, may not be null
91 * @return a new FluentIterable wrapping the provided iterable
92 * @throws NullPointerException if iterable is null
93 */
94 public static <T> FluentIterable<T> of(final Iterable<T> iterable) {
95 Objects.requireNonNull(iterable, "iterable");
96 if (iterable instanceof FluentIterable<?>) {
97 return (FluentIterable<T>) iterable;
98 }
99 return new FluentIterable<>(iterable);
100 }
101
102 /**
103 * Creates a new FluentIterable of the single provided element.
104 * <p>
105 * The returned iterable's iterator does not support {@code remove()}.
106 * </p>
107 *
108 * @param <T> the element type
109 * @param singleton the singleton element
110 * @return a new FluentIterable containing the singleton
111 */
112 public static <T> FluentIterable<T> of(final T singleton) {
113 return of(IteratorUtils.asIterable(new SingletonIterator<>(singleton, false)));
114 }
115
116 /**
117 * Creates a new FluentIterable from the provided elements.
118 * <p>
119 * The returned iterable's iterator does not support {@code remove()}.
120 * </p>
121 *
122 * @param <T> the element type
123 * @param elements the elements to be contained in the FluentIterable
124 * @return a new FluentIterable containing the provided elements
125 */
126 public static <T> FluentIterable<T> of(final T... elements) {
127 return of(Arrays.asList(elements));
128 }
129
130 /** A reference to the wrapped iterable. */
131 private final Iterable<E> iterable;
132
133 /**
134 * Don't allow instances.
135 */
136 FluentIterable() {
137 iterable = this;
138 }
139
140 /**
141 * Create a new FluentIterable by wrapping the provided iterable.
142 * @param iterable the iterable to wrap
143 */
144 private FluentIterable(final Iterable<E> iterable) {
145 this.iterable = iterable;
146 }
147
148 /**
149 * Checks if all elements contained in this iterable are matching the
150 * provided predicate.
151 * <p>
152 * A {@code null} or empty iterable returns true.
153 * </p>
154 *
155 * @param predicate the predicate to use, may not be null
156 * @return true if all elements contained in this iterable match the predicate,
157 * false otherwise
158 * @throws NullPointerException if predicate is null
159 */
160 public boolean allMatch(final Predicate<? super E> predicate) {
161 return IterableUtils.matchesAll(iterable, predicate);
162 }
163
164 /**
165 * Checks if this iterable contains any element matching the provided predicate.
166 * <p>
167 * A {@code null} or empty iterable returns false.
168 * </p>
169 *
170 * @param predicate the predicate to use, may not be null
171 * @return true if at least one element contained in this iterable matches the predicate,
172 * false otherwise
173 * @throws NullPointerException if predicate is null
174 */
175 public boolean anyMatch(final Predicate<? super E> predicate) {
176 return IterableUtils.matchesAny(iterable, predicate);
177 }
178
179 /**
180 * Returns a new FluentIterable whose iterator will first traverse
181 * the elements of the current iterable, followed by the provided
182 * elements.
183 *
184 * @param elements the elements to append to the iterable
185 * @return a new iterable, combining this iterable with the elements
186 */
187 public FluentIterable<E> append(final E... elements) {
188 return append(Arrays.asList(elements));
189 }
190
191 /**
192 * Returns a new FluentIterable whose iterator will first traverse
193 * the elements of the current iterable, followed by the elements
194 * of the provided iterable.
195 *
196 * @param other the other iterable to combine, may not be null
197 * @return a new iterable, combining this iterable with other
198 * @throws NullPointerException if other is null
199 */
200 public FluentIterable<E> append(final Iterable<? extends E> other) {
201 return of(IterableUtils.chainedIterable(iterable, other));
202 }
203
204 /**
205 * Returns an Enumeration that will enumerate all elements contained
206 * in this iterable.
207 *
208 * @return an Enumeration over the elements of this iterable
209 */
210 public Enumeration<E> asEnumeration() {
211 return IteratorUtils.asEnumeration(iterator());
212 }
213
214 /**
215 * Returns a new FluentIterable whose iterator will traverse the
216 * elements of the current and provided iterable in natural order.
217 * <p>
218 * Example: natural ordering
219 * </p>
220 * <ul>
221 * <li>this contains elements [1, 3, 5, 7]
222 * <li>other contains elements [2, 4, 6, 8]
223 * </ul>
224 * <p>
225 * The returned iterable will traverse the elements in the following
226 * order: [1, 2, 3, 4, 5, 6, 7, 8]
227 * </p>
228 *
229 * @param other the other iterable to collate, may not be null
230 * @return a new iterable, collating this iterable with the other in natural order
231 * @throws NullPointerException if other is null
232 * @see org.apache.commons.collections4.iterators.CollatingIterator
233 */
234 public FluentIterable<E> collate(final Iterable<? extends E> other) {
235 return of(IterableUtils.collatedIterable(iterable, other));
236 }
237
238 /**
239 * Returns a new FluentIterable whose iterator will traverse the
240 * elements of the current and provided iterable according to the
241 * ordering defined by a comparator.
242 * <p>
243 * Example: descending order
244 * </p>
245 * <ul>
246 * <li>this contains elements [7, 5, 3, 1]
247 * <li>other contains elements [8, 6, 4, 2]
248 * </ul>
249 * <p>
250 * The returned iterable will traverse the elements in the following
251 * order: [8, 7, 6, 5, 4, 3, 2, 1]
252 * </p>
253 *
254 * @param comparator the comparator to define an ordering, may be null,
255 * in which case natural ordering will be used
256 * @param other the other iterable to collate, may not be null
257 * @return a new iterable, collating this iterable with the other in natural order
258 * @throws NullPointerException if other is null
259 * @see org.apache.commons.collections4.iterators.CollatingIterator
260 */
261 public FluentIterable<E> collate(final Iterable<? extends E> other,
262 final Comparator<? super E> comparator) {
263 return of(IterableUtils.collatedIterable(comparator, iterable, other));
264 }
265
266 /**
267 * Checks if the object is contained in this iterable.
268 *
269 * @param object the object to check
270 * @return true if the object is contained in this iterable, false otherwise
271 */
272 public boolean contains(final Object object) {
273 return IterableUtils.contains(iterable, object);
274 }
275
276 /**
277 * Traverses an iterator of this iterable and adds all elements
278 * to the provided collection.
279 *
280 * @param collection the collection to add the elements
281 * @throws NullPointerException if collection is null
282 */
283 public void copyInto(final Collection<? super E> collection) {
284 Objects.requireNonNull(collection, "collection");
285 CollectionUtils.addAll(collection, iterable);
286 }
287
288 /**
289 * This method fully traverses an iterator of this iterable and returns
290 * a new iterable with the same contents, but without any reference
291 * to the originating iterables and/or iterators.
292 * <p>
293 * Calling this method is equivalent to:
294 * </p>
295 * <pre>
296 * FluentIterable<E> someIterable = ...;
297 * FluentIterable.of(someIterable.toList());
298 * </pre>
299 *
300 * @return a new iterable with the same contents as this iterable
301 */
302 public FluentIterable<E> eval() {
303 return of(toList());
304 }
305
306 /**
307 * Returns a new FluentIterable whose iterator will only return
308 * elements from this iterable matching the provided predicate.
309 *
310 * @param predicate the predicate used to filter elements
311 * @return a new iterable, providing a filtered view of this iterable
312 * @throws NullPointerException if predicate is null
313 */
314 public FluentIterable<E> filter(final Predicate<? super E> predicate) {
315 return of(IterableUtils.filteredIterable(iterable, predicate));
316 }
317
318 /**
319 * Applies the closure to all elements contained in this iterable.
320 *
321 * @param closure the closure to apply to each element, may not be null
322 * @throws NullPointerException if closure is null
323 */
324 public void forEach(final Closure<? super E> closure) {
325 IterableUtils.forEach(iterable, closure);
326 }
327
328 /**
329 * Gets the element at the provided position in this iterable.
330 * In order to return the element, an iterator needs to be traversed
331 * up to the requested position.
332 *
333 * @param position the position of the element to return
334 * @return the element
335 * @throws IndexOutOfBoundsException if the provided position is outside the
336 * valid range of this iterable: [0, size)
337 */
338 public E get(final int position) {
339 return IterableUtils.get(iterable, position);
340 }
341
342 /**
343 * Checks if this iterable is empty.
344 *
345 * @return true if this iterable does not contain any elements, false otherwise
346 */
347 public boolean isEmpty() {
348 return IterableUtils.isEmpty(iterable);
349 }
350
351 /** {@inheritDoc} */
352 @Override
353 public Iterator<E> iterator() {
354 return iterable.iterator();
355 }
356
357 /**
358 * Returns a new FluentIterable whose iterator will return at most
359 * the provided maximum number of elements from this iterable.
360 *
361 * @param maxSize the maximum number of elements
362 * @return a new iterable, providing a bounded view of this iterable
363 * @throws IllegalArgumentException if maxSize is negative
364 */
365 public FluentIterable<E> limit(final long maxSize) {
366 return of(IterableUtils.boundedIterable(iterable, maxSize));
367 }
368
369 /**
370 * Returns a new FluentIterable whose iterator will loop infinitely
371 * over the elements from this iterable.
372 *
373 * @return a new iterable, providing a looping view of this iterable
374 */
375 public FluentIterable<E> loop() {
376 return of(IterableUtils.loopingIterable(iterable));
377 }
378
379 /**
380 * Returns a new FluentIterable whose iterator will traverse the
381 * elements from this iterable in reverse order.
382 *
383 * @return a new iterable, providing a reversed view of this iterable
384 */
385 public FluentIterable<E> reverse() {
386 return of(IterableUtils.reversedIterable(iterable));
387 }
388
389 /**
390 * Returns the number of elements that are contained in this iterable.
391 * In order to determine the size, an iterator needs to be traversed.
392 *
393 * @return the size of this iterable
394 */
395 public int size() {
396 return IterableUtils.size(iterable);
397 }
398
399 /**
400 * Returns a new FluentIterable whose iterator will skip the first
401 * N elements from this iterable.
402 *
403 * @param elementsToSkip the number of elements to skip
404 * @return a new iterable, providing a view of this iterable by skipping
405 * the first N elements
406 * @throws IllegalArgumentException if elementsToSkip is negative
407 */
408 public FluentIterable<E> skip(final long elementsToSkip) {
409 return of(IterableUtils.skippingIterable(iterable, elementsToSkip));
410 }
411
412 /**
413 * Returns an array containing all elements of this iterable by traversing
414 * its iterator.
415 *
416 * @param arrayClass the class of array to create
417 * @return an array of the iterable contents
418 * @throws ArrayStoreException if arrayClass is invalid
419 */
420 public E[] toArray(final Class<E> arrayClass) {
421 return IteratorUtils.toArray(iterator(), arrayClass);
422 }
423
424 /**
425 * Returns a mutable list containing all elements of this iterable
426 * by traversing its iterator.
427 * <p>
428 * The returned list is guaranteed to be mutable.
429 * </p>
430 *
431 * @return a list of the iterable contents
432 */
433 public List<E> toList() {
434 return IterableUtils.toList(iterable);
435 }
436
437 /** {@inheritDoc} */
438 @Override
439 public String toString() {
440 return IterableUtils.toString(iterable);
441 }
442
443 /**
444 * Returns a new FluentIterable whose iterator will return all elements
445 * of this iterable transformed by the provided transformer.
446 *
447 * @param <O> the output element type
448 * @param transformer the transformer applied to each element
449 * @return a new iterable, providing a transformed view of this iterable
450 * @throws NullPointerException if transformer is null
451 */
452 public <O> FluentIterable<O> transform(final Transformer<? super E, ? extends O> transformer) {
453 return of(IterableUtils.transformedIterable(iterable, transformer));
454 }
455
456 /**
457 * Returns a new FluentIterable whose iterator will return a unique view
458 * of this iterable.
459 *
460 * @return a new iterable, providing a unique view of this iterable
461 */
462 public FluentIterable<E> unique() {
463 return of(IterableUtils.uniqueIterable(iterable));
464 }
465
466 /**
467 * Returns a new FluentIterable whose iterator will return an unmodifiable
468 * view of this iterable.
469 *
470 * @return a new iterable, providing an unmodifiable view of this iterable
471 */
472 public FluentIterable<E> unmodifiable() {
473 return of(IterableUtils.unmodifiableIterable(iterable));
474 }
475
476 /**
477 * Returns a new FluentIterable whose iterator will traverse
478 * the elements of this iterable and the other iterable in
479 * alternating order.
480 *
481 * @param other the other iterable to interleave, may not be null
482 * @return a new iterable, interleaving this iterable with others
483 * @throws NullPointerException if other is null
484 */
485 public FluentIterable<E> zip(final Iterable<? extends E> other) {
486 return of(IterableUtils.zippingIterable(iterable, other));
487 }
488
489 /**
490 * Returns a new FluentIterable whose iterator will traverse
491 * the elements of this iterable and the other iterables in
492 * alternating order.
493 *
494 * @param others the iterables to interleave, may not be null
495 * @return a new iterable, interleaving this iterable with others
496 * @throws NullPointerException if either of the provided iterables is null
497 */
498 public FluentIterable<E> zip(final Iterable<? extends E>... others) {
499 return of(IterableUtils.zippingIterable(iterable, others));
500 }
501
502 }