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); + } +}