Emulate new methods for Comparator in Java 8.
Started from https://gwt-review.googlesource.com/#/c/13730/
Change-Id: I0d4c6da1a260fb3d89cf528d604b1c632d5adba4
diff --git a/user/super/com/google/gwt/emul/java/util/Comparator.java b/user/super/com/google/gwt/emul/java/util/Comparator.java
index 563608b..e0a9813 100644
--- a/user/super/com/google/gwt/emul/java/util/Comparator.java
+++ b/user/super/com/google/gwt/emul/java/util/Comparator.java
@@ -15,6 +15,14 @@
*/
package java.util;
+import static javaemul.internal.InternalPreconditions.checkCriticalNotNull;
+
+import java.io.Serializable;
+import java.util.function.Function;
+import java.util.function.ToDoubleFunction;
+import java.util.function.ToIntFunction;
+import java.util.function.ToLongFunction;
+
/**
* An interface used a basis for implementing custom ordering. <a
* href="http://java.sun.com/j2se/1.5.0/docs/api/java/util/Comparator.html">[Sun
@@ -22,6 +30,7 @@
*
* @param <T> the type to be compared.
*/
+@FunctionalInterface
public interface Comparator<T> {
int compare(T a, T b);
@@ -29,4 +38,84 @@
@Override
boolean equals(Object other);
+ default Comparator<T> reversed() {
+ return (a, b) -> compare(b, a);
+ }
+
+ default Comparator<T> thenComparing(Comparator<? super T> other) {
+ checkCriticalNotNull(other);
+ return (Comparator<T> & Serializable) (a, b) -> {
+ int c = compare(a, b);
+ return (c != 0) ? c : other.compare(a, b);
+ };
+ }
+
+ default <U> Comparator<T> thenComparing(Function<? super T, ? extends U> keyExtractor,
+ Comparator<? super U> keyComparator) {
+ return thenComparing(comparing(keyExtractor, keyComparator));
+ }
+
+ default <U extends Comparable<? super U>> Comparator<T> thenComparing(
+ Function<? super T, ? extends U> keyExtractor) {
+ return thenComparing(comparing(keyExtractor));
+ }
+
+ default Comparator<T> thenComparingInt(ToIntFunction<? super T> keyExtractor) {
+ return thenComparing(comparingInt(keyExtractor));
+ }
+
+ default Comparator<T> thenComparingLong(ToLongFunction<? super T> keyExtractor) {
+ return thenComparing(comparingLong(keyExtractor));
+ }
+
+ default Comparator<T> thenComparingDouble(ToDoubleFunction<? super T> keyExtractor) {
+ return thenComparing(comparingDouble(keyExtractor));
+ }
+
+ static <T, U> Comparator<T> comparing(Function<? super T, ? extends U> keyExtractor,
+ Comparator<? super U> keyComparator) {
+ checkCriticalNotNull(keyExtractor);
+ checkCriticalNotNull(keyComparator);
+ return (Comparator<T> & Serializable) (a, b) ->
+ keyComparator.compare(keyExtractor.apply(a), keyExtractor.apply(b));
+ }
+
+ static <T, U extends Comparable<? super U>> Comparator<T> comparing(
+ Function<? super T, ? extends U> keyExtractor) {
+ return comparing(keyExtractor, naturalOrder());
+ }
+
+ static<T> Comparator<T> comparingDouble(ToDoubleFunction<? super T> keyExtractor) {
+ checkCriticalNotNull(keyExtractor);
+ return (Comparator<T> & Serializable) (a, b) ->
+ Double.compare(keyExtractor.applyAsDouble(a), keyExtractor.applyAsDouble(b));
+ }
+
+ static <T> Comparator<T> comparingInt(ToIntFunction<? super T> keyExtractor) {
+ checkCriticalNotNull(keyExtractor);
+ return (Comparator<T> & Serializable) (a, b) ->
+ Integer.compare(keyExtractor.applyAsInt(a), keyExtractor.applyAsInt(b));
+ }
+
+ static <T> Comparator<T> comparingLong(ToLongFunction<? super T> keyExtractor) {
+ checkCriticalNotNull(keyExtractor);
+ return (Comparator<T> & Serializable) (a, b) ->
+ Long.compare(keyExtractor.applyAsLong(a), keyExtractor.applyAsLong(b));
+ }
+
+ static <T extends Comparable<? super T>> Comparator<T> naturalOrder() {
+ return Comparators.natural();
+ }
+
+ static <T> Comparator<T> nullsFirst(Comparator<? super T> comparator) {
+ return new Comparators.NullComparator<>(true, comparator);
+ }
+
+ static <T> Comparator<T> nullsLast(Comparator<? super T> comparator) {
+ return new Comparators.NullComparator<>(false, comparator);
+ }
+
+ static <T extends Comparable<? super T>> Comparator<T> reverseOrder() {
+ return Comparators.reverseOrder();
+ }
}
diff --git a/user/super/com/google/gwt/emul/java/util/Comparators.java b/user/super/com/google/gwt/emul/java/util/Comparators.java
index e575a80..b01804c 100644
--- a/user/super/com/google/gwt/emul/java/util/Comparators.java
+++ b/user/super/com/google/gwt/emul/java/util/Comparators.java
@@ -17,31 +17,57 @@
import static javaemul.internal.InternalPreconditions.checkNotNull;
+import java.io.Serializable;
+
class Comparators {
/*
- * This is a utility class that provides a default Comparator. This class
+ * This is a utility class that provides default Comparators. This class
* exists so Arrays and Collections can share the natural comparator without
* having to know internals of each other.
*
* This class is package protected since it is not in the JRE.
*/
- /**
- * Compares two Objects according to their <i>natural ordering</i>.
- *
- * @see java.lang.Comparable
- */
- private static final Comparator<Object> NATURAL = new Comparator<Object>() {
- @Override
- public int compare(Object o1, Object o2) {
- checkNotNull(o1);
- checkNotNull(o2);
- return ((Comparable<Object>) o1).compareTo(o2);
+ private static final Comparator<Comparable<Object>> NATURAL = (a, b) -> checkNotNull(a).compareTo(checkNotNull(b));
+
+ private static final Comparator<Comparable<Object>> REVERSE_ORDER = (a, b) -> checkNotNull(b).compareTo(checkNotNull(a));
+
+ static final class NullComparator<T> implements Comparator<T>, Serializable {
+ private final boolean nullFirst;
+ private final Comparator<T> delegate;
+
+ @SuppressWarnings("unchecked")
+ NullComparator(boolean nullFirst, Comparator<? super T> delegate) {
+ this.nullFirst = nullFirst;
+ this.delegate = (Comparator<T>) delegate;
}
- };
+
+ @Override
+ public int compare(T a, T b) {
+ if (a == null) {
+ return b == null ? 0 : (nullFirst ? -1 : 1);
+ }
+ if (b == null) {
+ return nullFirst ? 1 : -1;
+ }
+ return delegate == null ? 0 : delegate.compare(a, b);
+ }
+
+ @Override
+ public Comparator<T> reversed() {
+ return new NullComparator<>(!nullFirst, delegate == null ? null : delegate.reversed());
+ }
+
+ @Override
+ public Comparator<T> thenComparing(Comparator<? super T> other) {
+ return new NullComparator<>(nullFirst, delegate == null ?
+ other : delegate.thenComparing(other));
+ }
+ }
/**
- * Returns the natural Comparator.
+ * Returns the natural Comparator which compares two Objects
+ * according to their <i>natural ordering</i>.
* <p>
* Example:
*
@@ -49,7 +75,13 @@
*
* @return the natural Comparator
*/
+ @SuppressWarnings("unchecked")
public static <T> Comparator<T> natural() {
return (Comparator<T>) NATURAL;
}
+
+ @SuppressWarnings("unchecked")
+ public static <T> Comparator<T> reverseOrder() {
+ return (Comparator<T>) REVERSE_ORDER;
+ }
}
diff --git a/user/test/com/google/gwt/emultest/EmulJava8Suite.java b/user/test/com/google/gwt/emultest/EmulJava8Suite.java
index 09394ae..7b311b3 100644
--- a/user/test/com/google/gwt/emultest/EmulJava8Suite.java
+++ b/user/test/com/google/gwt/emultest/EmulJava8Suite.java
@@ -15,6 +15,7 @@
*/
package com.google.gwt.emultest;
+import com.google.gwt.emultest.java8.util.ComparatorTest;
import com.google.gwt.emultest.java8.util.DoubleSummaryStatisticsTest;
import com.google.gwt.emultest.java8.util.IntSummaryStatisticsTest;
import com.google.gwt.emultest.java8.util.LongSummaryStatisticsTest;
@@ -37,6 +38,7 @@
GWTTestSuite suite = new GWTTestSuite("Tests for com.google.gwt.emul.java8");
//-- java.util
+ suite.addTestSuite(ComparatorTest.class);
suite.addTestSuite(OptionalTest.class);
suite.addTestSuite(OptionalIntTest.class);
suite.addTestSuite(OptionalLongTest.class);
diff --git a/user/test/com/google/gwt/emultest/java8/util/ComparatorTest.java b/user/test/com/google/gwt/emultest/java8/util/ComparatorTest.java
new file mode 100644
index 0000000..9460571
--- /dev/null
+++ b/user/test/com/google/gwt/emultest/java8/util/ComparatorTest.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.emultest.java8.util;
+
+import com.google.gwt.emultest.java.util.EmulTestBase;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+/**
+ * Java 8 methods to test in java.util.Comparator.
+ */
+public class ComparatorTest extends EmulTestBase {
+
+ public void testThenComparing() {
+ Supplier<String[]> strings = () -> new String[] {"1,b", "1,a", "2,a"};
+ // expected sort results for 1st and 2nd char, each (f)orward or (r)everse
+ String[] f1f2 = {"1,a", "1,b", "2,a"};
+ String[] f1r2 = {"1,b", "1,a", "2,a"};
+ String[] f2f1 = {"1,a", "2,a" ,"1,b"};
+
+ // keyextractor
+ assertSortedEquals(f1f2, strings,
+ Comparator.<String, String>comparing(s -> s.split(",")[0])
+ .thenComparing(s -> s.split(",")[1])
+ );
+ // keyextractor, keycomparator
+ assertSortedEquals(f1r2, strings,
+ Comparator.<String, String>comparing(s -> s.split(",")[0])
+ .thenComparing(
+ s -> s.split(",")[1],
+ Comparator.<String>reverseOrder()
+ )
+ );
+
+ // int key extractor
+ assertSortedEquals(f2f1, strings,
+ Comparator.<String, String>comparing(s -> s.split(",")[1])
+ .thenComparingInt(
+ s -> Integer.parseInt(s.split(",")[0])
+ )
+ );
+ // long key extractor
+ assertSortedEquals(f2f1, strings,
+ Comparator.<String, String>comparing(s -> s.split(",")[1])
+ .thenComparingLong(
+ s -> Long.parseLong(s.split(",")[0])
+ )
+ );
+ // double key extractor
+ assertSortedEquals(f2f1, strings,
+ Comparator.<String, String>comparing(s -> s.split(",")[1])
+ .thenComparingDouble(
+ s -> Double.parseDouble(s.split(",")[0])
+ )
+ );
+ }
+
+ public void testComparing() {
+ Supplier<String[]> strings = () -> new String[] {"1b3", "2a1", "3c2"};
+ // pre-sorted lists to test against, named for which char (1-indexed) they are sorted on
+ String[] first = {"1b3", "2a1", "3c2"};
+ String[] second = {"2a1", "1b3", "3c2"};
+ String[] secondReversed = {"3c2", "1b3", "2a1"};
+ String[] third = {"2a1", "3c2", "1b3"};
+
+ // keyextractor
+ assertSortedEquals(first, strings, Comparator.comparing(Function.identity()));
+ assertSortedEquals(second, strings, Comparator.comparing(a -> a.substring(1)));
+ assertSortedEquals(third, strings, Comparator.comparing(a -> a.substring(2)));
+
+ // keyextractor, keycomparator
+ assertSortedEquals(secondReversed, strings, Comparator.comparing(a -> a.substring(1), Comparator.<String>reverseOrder()));
+
+ // double key extractor
+ assertSortedEquals(third, strings, Comparator.comparingDouble(a -> Double.parseDouble(a.substring(2))));
+ // int key extractor
+ assertSortedEquals(third, strings, Comparator.comparingInt(a -> Integer.parseInt(a.substring(2))));
+ // long key extractor
+ assertSortedEquals(third, strings, Comparator.comparingLong(a -> Long.parseLong(a.substring(2))));
+ }
+
+ public void testNullsFirst() {
+ Supplier<String[]> strings = () -> new String[]{"a", null, "c", null, "b"};
+ assertSortedEquals(
+ new String[] {null, null, "a", "b", "c"},
+ strings,
+ Comparator.nullsFirst(Comparator.naturalOrder())
+ );
+ assertSortedEquals(
+ new String[] {null, null, "c", "b", "a"},
+ strings,
+ Comparator.nullsFirst(Comparator.reverseOrder())
+ );
+ }
+
+ public void testNullsLast() {
+ Supplier<String[]> strings = () -> new String[]{"a", null, "c", null, "b"};
+ assertSortedEquals(
+ new String[] {"a", "b", "c", null, null},
+ strings,
+ Comparator.nullsLast(Comparator.naturalOrder())
+ );
+ assertSortedEquals(
+ new String[] {"c", "b", "a", null, null},
+ strings,
+ Comparator.nullsLast(Comparator.reverseOrder())
+ );
+ }
+
+ private static void assertSortedEquals(String[] expected, Supplier<String[]> presort, Comparator<String> comparator) {
+ String[] presortedArray = presort.get();
+ Arrays.sort(presortedArray, comparator);
+ assertEquals(expected, presortedArray);
+ }
+}