iterable) {
+ FluentIterable::from(iterable).last
+ }
+
+ /**
+ * Returns an {@link Optional} containing the first element in this iterable that
+ * satisfies the given predicate, if such an element exists.
+ *
+ * Warning: avoid using a {@code predicate} that matches {@code null}. If {@code null}
+ * is matched in this fluent iterable, a {@link NullPointerException} will be thrown.
+ */
+ def static Optional findFirstOptional(Iterable iterable, (T) => boolean predicate) {
+ Iterables::tryFind(iterable, new BooleanFunctionDelegate(predicate))
+ }
+
+ /**
+ * Builds a new iterable by applying a function to all elements of this iterable
+ * and using the elements of the resulting iterables.
+ *
+ * For example:
+ *
+ *
{@code val words = lines.flatMap[split("\\W+").toList]}
+ */
+ def static Iterable flatMap(Iterable iterable, (T) => Iterable extends U> function) {
+ iterable.map(function).flatten
+ }
+
+ /**
+ * Counts the number of elements in this iterable which satisfy a predicate.
+ */
+ def static int count(Iterable iterable, (T) => boolean predicate) {
+ var count = 0
+ for (element : iterable) {
+ if (predicate.apply(element)) {
+ count = count + 1
+ }
+ }
+
+ count
+ }
+
+ /**
+ * Produces the range of all indices of this iterable.
+ *
+ * @return a range from 0 to one less than the size of this iterable.
+ */
+ def static ExclusiveRange indices(Iterable> iterable) {
+ 0 ..< iterable.size
+ }
+
+ /**
+ * Returns an iterable whose {@code Iterator} cycles indefinitely over the elements of
+ * this iterable.
+ *
+ * That iterator supports {@code remove()} if {@code iterable.iterator()} does. After
+ * {@code remove()} is called, subsequent cycles omit the removed element, which is no longer in
+ * this iterable. The iterator's {@code hasNext()} method returns {@code true} until
+ * this iterable is empty.
+ *
+ *
Warning: Typical uses of the resulting iterator may produce an infinite loop. You
+ * should use an explicit {@code break} or be certain that you will eventually remove all the
+ * elements.
+ */
+ def static Iterable cycle(Iterable iterable) {
+ Iterables::cycle(iterable)
+ }
+
+ /**
+ * Produces a new iterable which contains all elements of this iterable and also all elements of
+ * a given iterable. The source iterators are not polled until necessary.
+ *
+ * The returned iterable's iterator supports {@code remove()} when the
+ * corresponding input iterator supports it.
+ */
+ def static Iterable union(Iterable extends T> a, Iterable extends T> b) {
+ Iterables::concat(a, b)
+ }
+
+ /**
+ * Returns an iterable formed from this iterable and another iterable by combining
+ * corresponding elements in pairs. If one of the two collections is longer than the other,
+ * its remaining elements are ignored. The source iterators are not polled until necessary.
+ *
+ * The resulting iterable's iterator does not support {@code remove()}.
+ */
+ def static Iterable> zip(Iterable a, Iterable b) {
+ val FluentIterable> result = [|
+ val iterator1 = a.iterator
+ val iterator2 = b.iterator
+
+ val AbstractIterator> iterator = [|
+ if (iterator1.hasNext && iterator2.hasNext) {
+ iterator1.next -> iterator2.next
+ } else {
+ self.endOfData
+ }
+ ]
+
+ iterator
+ ]
+
+ result
+ }
+
+ /**
+ * Converts this iterable of pairs into two lists of the first and second
+ * half of each pair.
+ *
+ * The resulting lists are unmodifiable.
+ */
+ def static Pair, List> unzip(Iterable> iterable) {
+ val size = iterable.size
+ val List a = Lists::newArrayListWithCapacity(size)
+ val List b = Lists::newArrayListWithCapacity(size)
+
+ for (Pair pair : iterable) {
+ a.add(pair.key)
+ b.add(pair.value)
+ }
+
+ a.unmodifiableView -> b.unmodifiableView
+ }
+
+ /**
+ * Zips this iterable with its indices.
+ *
+ * The resulting iterable's iterator does not support {@code remove()}.
+ */
+ def static Iterable> zipWithIndex(Iterable iterable) {
+ iterable.zip(iterable.indices)
+ }
+
+ /**
+ * Returns the minimum element of this iterable, according to the natural ordering
+ * of its elements. All elements in the iterable must implement the Comparable
+ * interface.
+ *
+ * @throws NoSuchElementException if the iterable is empty.
+ */
+ def static > T min(Iterable iterable) {
+ iterable.min(Ordering::natural)
+ }
+
+ /**
+ * Returns the minimum element of the given iterable, according to the order induced by
+ * the specified comparator.
+ *
+ * @throws NoSuchElementException if the iterable is empty.
+ */
+ def static T min(Iterable iterable, Comparator super T> comp) {
+ val i = iterable.iterator
+ var min = i.next
+
+ while (i.hasNext) {
+ val next = i.next
+ if (comp.compare(next, min) < 0) {
+ min = next
+ }
+ }
+
+ min
+ }
+
+ /**
+ * Returns the minimum element of the given iterable based on the given {@code transformation},
+ * according to the natural ordering of the values.
+ *
+ * @throws NoSuchElementException if the iterable is empty.
+ */
+ def static > T minBy(Iterable iterable, (T) => U function) {
+ iterable.minBy(Ordering::natural, function)
+ }
+
+ /**
+ * Returns the minimum element of the given iterable based on the given {@code transformation},
+ * according to the order induced by the specified comparator.
+ *
+ * @throws NoSuchElementException if the iterable is empty.
+ */
+ def static T minBy(Iterable iterable, Comparator super U> comp, (T) => U function) {
+ val i = iterable.iterator
+ var min = i.next
+
+ while (i.hasNext) {
+ val next = i.next
+ if (comp.compare(function.apply(next), function.apply(min)) < 0) {
+ min = next
+ }
+ }
+
+ min
+ }
+
+ /**
+ * Returns the maximum element of this iterable, according to the natural ordering
+ * of its elements. All elements in the iterable must implement the Comparable
+ * interface.
+ *
+ * @throws NoSuchElementException if the iterable is empty.
+ */
+ def static > T max(Iterable iterable) {
+ iterable.max(Ordering::natural)
+ }
+
+ /**
+ * Returns the maximum element of the given iterable, according to the order induced by
+ * the specified comparator.
+ *
+ * @throws NoSuchElementException if the iterable is empty.
+ */
+ def static T max(Iterable iterable, Comparator super T> comp) {
+ val i = iterable.iterator
+ var max = i.next
+
+ while (i.hasNext) {
+ val next = i.next
+ if (comp.compare(next, max) > 0) {
+ max = next
+ }
+ }
+
+ max
+ }
+
+ /**
+ * Returns the maximum element of the given iterable based on the given {@code transformation},
+ * according to the natural ordering of the values.
+ *
+ * @throws NoSuchElementException if the iterable is empty.
+ */
+ def static > T maxBy(Iterable iterable, (T) => U function) {
+ iterable.maxBy(Ordering::natural, function)
+ }
+
+ /**
+ * Returns the maximum element of the given iterable based on the given {@code transformation},
+ * according to the order induced by the specified comparator.
+ *
+ * @throws NoSuchElementException if the iterable is empty.
+ */
+ def static T maxBy(Iterable iterable, Comparator super U> comp, (T) => U function) {
+ val i = iterable.iterator
+ var max = i.next
+
+ while (i.hasNext) {
+ val next = i.next
+ if (comp.compare(function.apply(next), function.apply(max)) > 0) {
+ max = next
+ }
+ }
+
+ max
+ }
+
+ /**
+ * Partitions this iterable into a map of lists according to some discriminator function.
+ *
+ * The resulting map and lists are unmodifiable.
+ */
+ def static Map> groupBy(Iterable iterable, (T) => K function) {
+ val map = Maps::>newHashMap
+
+ for (elem : iterable) {
+ val key = function.apply(elem)
+ map.getOrElseUpdate(key, Lists::newArrayList).add(elem)
+ }
+
+ for (key : map.keySet) {
+ map.put(key, map.get(key).unmodifiableView)
+ }
+
+ map.unmodifiableView
+ }
+
+ /**
+ * Divides this iterable into unmodifiable sublists of the given size (the final list may be
+ * smaller). For example, grouping an iterable containing {@code [a, b, c, d, e]} with a group
+ * size of 3 yields {@code [[a, b, c], [d, e]]} -- an outer iterable containing two inner lists
+ * of three and two elements, all in the original order.
+ *
+ * Iterators returned by the returned iterable do not support the {@link Iterator#remove()} method.
+ * The returned lists implement {@link RandomAccess}, whether or not the input list does.
+ *
+ * @return an iterable of unmodifiable lists containing the elements of {@code iterable} divided
+ * into groups
+ * @throws IllegalArgumentException if {@code size} is nonpositive
+ */
+ def static Iterable> grouped(Iterable iterable, int size) {
+ Iterables::partition(iterable, size)
+ }
+}
diff --git a/src/main/java/com/github/xtension/MapExtensions.xtend b/src/main/java/com/github/xtension/MapExtensions.xtend
new file mode 100644
index 0000000..58f9fed
--- /dev/null
+++ b/src/main/java/com/github/xtension/MapExtensions.xtend
@@ -0,0 +1,33 @@
+package com.github.xtension
+
+import java.util.Map
+
+final class MapExtensions {
+
+ private new() {
+ }
+
+ /**
+ * Returns the value associated with a key, or a default value if the key is not contained in the map.
+ */
+ def static V1 getOrElse(Map map, K key, V1 defaultValue) {
+ if (map.containsKey(key)) {
+ map.get(key)
+ } else {
+ defaultValue
+ }
+ }
+
+ /**
+ * Returns the value associated with a key. If the given key is not contained in the map,
+ * stores a default value with the key in the map and returns that value.
+ */
+ def static V getOrElseUpdate(Map map, K key, V defaultValue) {
+ if (map.containsKey(key)) {
+ map.get(key)
+ } else {
+ map.put(key, defaultValue)
+ defaultValue
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/github/xtension/ObjectExtensions.xtend b/src/main/java/com/github/xtension/ObjectExtensions.xtend
new file mode 100644
index 0000000..8687349
--- /dev/null
+++ b/src/main/java/com/github/xtension/ObjectExtensions.xtend
@@ -0,0 +1,29 @@
+package com.github.xtension
+
+import com.google.common.annotations.Beta
+
+final class ObjectExtensions {
+
+ private new() {
+ }
+
+ /**
+ * Determines whether the value matches any value in the given list.
+ * Example:
+ *
{@code 'a'.in('a', 'b', 'c')} (returns {@code true})
+ */
+ @Beta
+ def static boolean in(T obj, T other, T other2, T... others) {
+ obj == other || obj == other2 || others.contains(obj)
+ }
+
+ /**
+ * Determines whether the value does not match any value in the given list.
+ * Example:
+ *
{@code 'a'.notIn('c', 'd', 'e')} (returns {@code true})
+ */
+ @Beta
+ def static boolean notIn(T obj, T other, T other2, T... others) {
+ obj != other && obj != other2 && !others.contains(obj)
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/github/xtension/OptionalExtensions.xtend b/src/main/java/com/github/xtension/OptionalExtensions.xtend
new file mode 100644
index 0000000..c46c145
--- /dev/null
+++ b/src/main/java/com/github/xtension/OptionalExtensions.xtend
@@ -0,0 +1,45 @@
+package com.github.xtension
+
+import com.google.common.base.Optional
+
+import static com.google.common.base.Preconditions.*
+
+final class OptionalExtensions {
+
+ private new() {
+ }
+
+ /**
+ * Returns an {@link Optional} containing the result of applying a function to the value of this
+ * {@code Optional} if it is nonempty. Otherwise returns an empty {@code Optional}.
+ */
+ def static Optional map(Optional optional, (T) => U function) {
+ if (optional.present) {
+ Optional::of(function.apply(optional.get))
+ } else {
+ Optional::absent
+ }
+ }
+
+ /**
+ * Returns the result of applying a function to the value of this {@code Optional} if it is nonempty.
+ * Otherwise returns an empty {@code Optional}. Slightly different from {@link #map} in that the function
+ * is expected to return an {@code Optional}.
+ */
+ def static Optional flatMap(Optional optional, (T) => Optional function) {
+ if (optional.present) {
+ checkNotNull(function.apply(optional.get))
+ } else {
+ Optional::absent
+ }
+ }
+
+ /**
+ * If this {@code Optional} is nonempty, invoke a procedure with the value, otherwise do nothing.
+ */
+ def static void ifPresent(Optional optional, (T) => void procedure) {
+ if (optional.present) {
+ procedure.apply(optional.get)
+ }
+ }
+}