1 /*- 2 * #%L 3 * io.earcam.unexceptional 4 * %% 5 * Copyright (C) 2016 - 2017 earcam 6 * %% 7 * SPDX-License-Identifier: (BSD-3-Clause OR EPL-1.0 OR Apache-2.0 OR MIT) 8 * 9 * You <b>must</b> choose to accept, in full - any individual or combination of 10 * the following licenses: 11 * <ul> 12 * <li><a href="https://opensource.org/licenses/BSD-3-Clause">BSD-3-Clause</a></li> 13 * <li><a href="https://www.eclipse.org/legal/epl-v10.html">EPL-1.0</a></li> 14 * <li><a href="https://www.apache.org/licenses/LICENSE-2.0">Apache-2.0</a></li> 15 * <li><a href="https://opensource.org/licenses/MIT">MIT</a></li> 16 * </ul> 17 * #L% 18 */ 19 package io.earcam.unexceptional; 20 21 import static io.earcam.unexceptional.Exceptional.apply; 22 23 import java.io.Serializable; //NOSONAR sonar false positive (suspect Sonar Squid is looking at bytecode here? as erasure of all but left-most bound where multiple bounds occur, see casts of return types in maxBy/minBy) 24 import java.util.Collections; 25 import java.util.Comparator; 26 import java.util.Objects; 27 28 /** 29 * A checked equivalent of {@link java.util.Comparator}, also extends {@link CheckedToIntBiFunction} 30 * 31 * @param <T> the type to compare 32 * @param <E> the type of Throwable declared 33 * 34 * @since 0.2.0 35 * 36 * @see java.util.Comparator 37 */ 38 @FunctionalInterface 39 public interface CheckedComparator<T, E extends Throwable> extends CheckedToIntBiFunction<T, T, E> { 40 41 public default int applyAsInt(T t, T u) throws E 42 { 43 return compare(t, u); 44 } 45 46 47 /** 48 * See {@link java.util.Comparator#compare(Object, Object)} 49 * 50 * @param o1 first object to compare. 51 * @param o2 second object to compare. 52 * @return a negative integer, zero, or a positive integer as the 53 * first argument is less than, equal to, or greater than the 54 * second, respectively. 55 * @throws Throwable any throwable 56 */ 57 public abstract int compare(T o1, T o2) throws E; 58 59 60 /** 61 * See {@link java.util.Comparator#reversed()} 62 * 63 * @return an unchecked comparator of the reverse order to {@code this} 64 */ 65 public default Comparator<T> reversed() 66 { 67 return Collections.reverseOrder(Exceptional.uncheckComparator(this)); 68 } 69 70 71 /** 72 * See {@link java.util.Comparator#thenComparing(Comparator)} 73 * 74 * @param other the next comparator to be chained (IFF {@code this.compare(a, b)} returns zero) 75 * @return the chained {@link CheckedComparator} 76 * @throws NullPointerException if {@code other} is {@code null} 77 */ 78 @SuppressWarnings("squid:S1905") // SonarQube false positive 79 public default CheckedComparator<T, E> thenComparing(/* @Nonnull */ CheckedComparator<? super T, ? extends E> other) 80 { 81 Objects.requireNonNull(other); 82 return (CheckedComparator<T, E> & Serializable) (c1, c2) -> { 83 int res = compare(c1, c2);// NOSONAR stfu false positive 84 return (res == 0) ? other.compare(c1, c2) : res; 85 }; 86 } 87 88 89 /** 90 * See {@link java.util.Comparator#thenComparing(java.util.function.Function, Comparator)} 91 * 92 * @param <U> the type of the sort key 93 * 94 * @param keyExtractor function used to extract sort key 95 * @param keyComparator comparator used to compare extracted key 96 * @return the chained {@link CheckedComparator} 97 * 98 * @see #thenComparing(CheckedComparator) 99 */ 100 public default <U> CheckedComparator<T, E> thenComparing(CheckedFunction<? super T, ? extends U, E> keyExtractor, 101 CheckedComparator<? super U, E> keyComparator) 102 { 103 return thenComparing(comparing(keyExtractor, keyComparator)); 104 } 105 106 107 /** 108 * See {@link java.util.Comparator#thenComparing(java.util.function.Function)} 109 * 110 * @param <U> the type of the {@link Comparable} sort key 111 * 112 * @param keyExtractor function used to extract sort key. 113 * @return the chained {@link CheckedComparator} 114 * 115 * @see #thenComparing(CheckedComparator) 116 */ 117 public default <U extends Comparable<? super U>> CheckedComparator<T, E> thenComparing(CheckedFunction<? super T, ? extends U, E> keyExtractor) 118 { 119 return thenComparing(comparing(keyExtractor)); 120 } 121 122 123 /** 124 * See {@link java.util.Comparator#thenComparingInt(java.util.function.ToIntFunction)} 125 * 126 * @param keyExtractor function used to extract primitive {@code int} sort key. 127 * @return the chained {@link CheckedComparator} 128 * 129 * @see #thenComparing(CheckedComparator) 130 */ 131 public default CheckedComparator<T, E> thenComparingInt(CheckedToIntFunction<? super T, ? extends E> keyExtractor) 132 { 133 return thenComparing(comparingInt(keyExtractor)); 134 } 135 136 137 /** 138 * See {@link java.util.Comparator#thenComparingLong(java.util.function.ToLongFunction)} 139 * 140 * @param keyExtractor function used to extract primitive {@code long} sort key. 141 * @return the chained {@link CheckedComparator} 142 * 143 * @see #thenComparing(CheckedComparator) 144 */ 145 public default CheckedComparator<T, E> thenComparingLong(CheckedToLongFunction<? super T, ? extends E> keyExtractor) 146 { 147 return thenComparing(comparingLong(keyExtractor)); 148 } 149 150 151 /** 152 * See {@link java.util.Comparator#thenComparingDouble(java.util.function.ToDoubleFunction)} 153 * 154 * @param keyExtractor function used to extract primitive {@code double} sort key. 155 * @return the chained {@link CheckedComparator} 156 * @see #thenComparing(CheckedComparator) 157 */ 158 public default CheckedComparator<T, E> thenComparingDouble(CheckedToDoubleFunction<? super T, ? extends E> keyExtractor) 159 { 160 return thenComparing(comparingDouble(keyExtractor)); 161 } 162 163 164 /** 165 * See {@link java.util.Comparator#comparing(java.util.function.Function, Comparator)} 166 * 167 * @param <T> the type of element to be compared 168 * @param <U> the type of the sort key 169 * 170 * @param keyExtractor function used to extract sort key. 171 * @param keyComparator comparator used to compare extracted key. 172 * @return a comparator that compares by an extracted key using the specified {@code CheckedComparator}. 173 * @throws NullPointerException if either argument is {@code null}. 174 */ 175 @SuppressWarnings("squid:S1905") // SonarQube false positive 176 public static <T, U, E extends Throwable> CheckedComparator<T, E> comparing( 177 /* @Nonnull */ CheckedFunction<? super T, ? extends U, ? extends E> keyExtractor, 178 /* @Nonnull */ CheckedComparator<? super U, ? extends E> keyComparator) 179 { 180 Objects.requireNonNull(keyExtractor); 181 Objects.requireNonNull(keyComparator); 182 return (CheckedComparator<T, E> & Serializable) (c1, c2) -> keyComparator.compare(apply(keyExtractor, c1), apply(keyExtractor, c2)); 183 } 184 185 186 /** 187 * See {@link java.util.Comparator#comparing(java.util.function.Function)} 188 * 189 * @param <T> the type of element to be compared. 190 * @param <U> the type of the sort key. 191 * @param keyExtractor the function used to extract the sort key. 192 * @return a comparator that compares by an extracted key. 193 * @throws NullPointerException if the argument is {@code null}. 194 */ 195 @SuppressWarnings("squid:S1905") // SonarQube false positive 196 public static <T, U extends Comparable<? super U>, E extends Throwable> CheckedComparator<T, E> comparing( 197 /* @Nonnull */ CheckedFunction<? super T, ? extends U, ? extends E> keyExtractor) 198 { 199 Objects.requireNonNull(keyExtractor); 200 return (CheckedComparator<T, E> & Serializable) (c1, c2) -> Exceptional.get(() -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2))); 201 } 202 203 204 /** 205 * See {@link java.util.Comparator#comparingInt(java.util.function.ToIntFunction)} 206 * 207 * @param <T> the type of element to be compared. 208 * 209 * @param keyExtractor the function used to extract primitive {@code int} sort key. 210 * @return a checked comparator that compares by an extracted key. 211 * @throws NullPointerException if the argument is {@code null}. 212 * 213 * @see #comparing(CheckedFunction) 214 */ 215 @SuppressWarnings("squid:S1905") // SonarQube false positive 216 public static <T, E extends Throwable> CheckedComparator<T, E> comparingInt(/* @Nonnull */ CheckedToIntFunction<? super T, ? extends E> keyExtractor) 217 { 218 Objects.requireNonNull(keyExtractor); 219 return (CheckedComparator<T, E> & Serializable) (c1, c2) -> Exceptional 220 .get(() -> Integer.compare(keyExtractor.applyAsInt(c1), keyExtractor.applyAsInt(c2))); 221 } 222 223 224 /** 225 * See {@link java.util.Comparator#comparingLong(java.util.function.ToLongFunction)} 226 * 227 * @param <T> the type of element to be compared. 228 * 229 * @param keyExtractor the function used to extract primitive {@code long} sort key. 230 * @return a checked comparator that compares by an extracted key. 231 * @throws NullPointerException if the argument is {@code null}. 232 * 233 * @see #comparing(CheckedFunction) 234 */ 235 @SuppressWarnings("squid:S1905") // SonarQube false positive 236 public static <T, E extends Throwable> CheckedComparator<T, E> comparingLong(/* @Nonnull */ CheckedToLongFunction<? super T, ? extends E> keyExtractor) 237 { 238 Objects.requireNonNull(keyExtractor); 239 return (CheckedComparator<T, E> & Serializable) (c1, c2) -> Exceptional 240 .get(() -> Long.compare(keyExtractor.applyAsLong(c1), keyExtractor.applyAsLong(c2))); 241 } 242 243 244 /** 245 * See {@link java.util.Comparator#comparingDouble(java.util.function.ToDoubleFunction)} 246 * 247 * @param <T> the type of element to be compared. 248 * 249 * @param keyExtractor the function used to extract primitive {@code double} sort key. 250 * @return a checked comparator that compares by an extracted key. 251 * @throws NullPointerException if the argument is {@code null}. 252 * 253 * @see #comparing(CheckedFunction) 254 */ 255 @SuppressWarnings("squid:S1905") // SonarQube false positive 256 public static <T, E extends Throwable> CheckedComparator<T, E> comparingDouble(/* @Nonnull */ CheckedToDoubleFunction<? super T, ? extends E> keyExtractor) 257 { 258 Objects.requireNonNull(keyExtractor); 259 return (CheckedComparator<T, E> & Serializable) (c1, c2) -> Exceptional 260 .get(() -> Double.compare(keyExtractor.applyAsDouble(c1), keyExtractor.applyAsDouble(c2))); 261 } 262 }