View Javadoc
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 }