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 }