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.uncheck; | |
22 | ||
23 | import java.io.IOException; | |
24 | import java.util.function.Supplier; | |
25 | ||
26 | /** | |
27 | * <p> | |
28 | * Succinctly handle {@link AutoCloseable}s that throw checked {@link Exception}s. | |
29 | * </p> | |
30 | * | |
31 | * <p> | |
32 | * There are two families of static methods; {@code closeAfterApplying(...)} and {@code closeAfterAccepting(...)}, the | |
33 | * former <i>applies</i> checked functions (the use-case being <b>read</b>) while the later <i>accepts</i> checked | |
34 | * consumers (the use-case being <b>write</b>) | |
35 | * </p> | |
36 | * | |
37 | * <p> | |
38 | * <b>Example</b>: this zero-branch, single-instruction, one-liner will find a free port number: | |
39 | * </p> | |
40 | * | |
41 | * <p> | |
42 | * <code> int port = Closing.closeAfterApplying(ServerSocket::new, 0, ServerSocket::getLocalPort);</code> | |
43 | * </p> | |
44 | * | |
45 | * <p> | |
46 | * <b>Motivation</b>: I/O stream instances typically (and necessarily) requires handling a lot of possible | |
47 | * {@link IOException}s - by invoking: | |
48 | * </p> | |
49 | * <ul> | |
50 | * <li>Constructor</li> | |
51 | * <li>A read/write call</li> | |
52 | * <li>A call to {@link AutoCloseable#close()}</li> | |
53 | * <li>A read/write call, then subsequently in the call to {@link AutoCloseable#close()} - via | |
54 | * {@code try}-with-resources</li> | |
55 | * </ul> | |
56 | * | |
57 | * <p> | |
58 | * That's a lot of branches to cover - often it is irrelevant to the application which branch actually throws, but | |
59 | * regardless test coverage suffers without some needless stub/mocked throw-on-create/read/write/close tests. | |
60 | * </p> | |
61 | * | |
62 | * @since 0.3.0 | |
63 | */ | |
64 | public final class Closing { | |
65 | ||
66 | private Closing() | |
67 | { | |
68 | throw new IllegalStateException("Why on earth would you want to instantiate this?"); | |
69 | } | |
70 | ||
71 | ||
72 | /** | |
73 | * <p> | |
74 | * Ultra-shorthand for {@link AutoCloseable}/{@link java.io.Closeable}, obvious use for {@link java.io.InputStream} | |
75 | * </p> | |
76 | * | |
77 | * @param create a function applying {@code t} to produce an {@link AutoCloseable} of type {@code <C>} | |
78 | * @param t the argument to apply to the {@code create} function | |
79 | * @param convert a function applied to the {@link AutoCloseable} to produce the result | |
80 | * | |
81 | * @param <C> {@link AutoCloseable} type | |
82 | * @param <T> {@code create} function argument type | |
83 | * @param <R> the result type | |
84 | * | |
85 | * @return the result of applying the {@code convert} function | |
86 | */ | |
87 | public static <C extends AutoCloseable, T, R> R closeAfterApplying(CheckedFunction<T, C, ?> create, T t, CheckedFunction<C, R, ?> convert) | |
88 | { | |
89 | C closeable = Exceptional.apply(create, t); | |
90 |
1
1. closeAfterApplying : mutated return of Object value for io/earcam/unexceptional/Closing::closeAfterApplying to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return closeAfterApplying(closeable, convert); |
91 | } | |
92 | ||
93 | ||
94 | /** | |
95 | * Applies the function to the closeable, returning the result and closing the closable - checked exceptions are | |
96 | * rethrown as unchecked. | |
97 | * | |
98 | * @param closeable the closeable subject of the {@code convert} function | |
99 | * @param convert the function consuming the closeable and supplying the result | |
100 | * | |
101 | * @param <C> the auto-closeable type, to apply and close | |
102 | * @param <R> the result type | |
103 | * | |
104 | * @return the result of applying {@code convert} function to the {@code closeable} argument | |
105 | */ | |
106 | public static <C extends AutoCloseable, R> R closeAfterApplying(C closeable, CheckedFunction<C, R, ?> convert) | |
107 | { | |
108 | try(C autoClose = closeable) { | |
109 |
1
1. closeAfterApplying : mutated return of Object value for io/earcam/unexceptional/Closing::closeAfterApplying to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return Exceptional.apply(convert, autoClose); |
110 | } catch(Exception e) { | |
111 | throw uncheck(e); | |
112 | } | |
113 | } | |
114 | ||
115 | ||
116 | /** | |
117 | * Applies the bi-function to the closeable, returning the result and closing the closable - checked exceptions are | |
118 | * rethrown as unchecked. | |
119 | * | |
120 | * @param closeable the closeable subject of the {@code convert} bi-function | |
121 | * @param instance the second argument for the bi-function | |
122 | * @param convert the function consuming the closeable and supplying the result | |
123 | * | |
124 | * @param <C> the auto-closeable type, will be applied and closed | |
125 | * @param <U> the type of second argument to bi-function apply | |
126 | * @param <R> the result type | |
127 | * | |
128 | * @return the result of applying {@code convert} function to the {@code closeable} argument | |
129 | */ | |
130 | public static <C extends AutoCloseable, U, R, E extends Throwable> R closeAfterApplying(C closeable, U instance, CheckedBiFunction<C, U, R, E> convert) | |
131 | { | |
132 | try(C autoClose = closeable) { | |
133 |
1
1. closeAfterApplying : mutated return of Object value for io/earcam/unexceptional/Closing::closeAfterApplying to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return Exceptional.apply(convert, autoClose, instance); |
134 | } catch(Exception e) { | |
135 | throw uncheck(e); | |
136 | } | |
137 | } | |
138 | ||
139 | ||
140 | /** | |
141 | * Applies the {@code create} function to {@code t}, resulting in a {@link AutoCloseable} which is closed after | |
142 | * being consumed. | |
143 | * Checked exceptions are rethrown as unchecked. | |
144 | * | |
145 | * @param create the function creating the {@link AutoCloseable} | |
146 | * @param t the argument that the {@code create} function is applied to | |
147 | * @param consume the consumer of the {@link AutoCloseable} | |
148 | * | |
149 | * @param <C> the auto-closeable type, to be created, consumed and closed | |
150 | * @param <T> the function's argument type, used to create the auto-closeable | |
151 | */ | |
152 | public static <C extends AutoCloseable, T> void closeAfterAccepting(CheckedFunction<T, C, ?> create, T t, CheckedConsumer<C, ?> consume) | |
153 | { | |
154 | C closeable = Exceptional.apply(create, t); | |
155 |
1
1. closeAfterAccepting : removed call to io/earcam/unexceptional/Closing::closeAfterAccepting → KILLED |
closeAfterAccepting(closeable, consume); |
156 | } | |
157 | ||
158 | ||
159 | /** | |
160 | * Consumes the {@code closeable} before closing. Checked exceptions are rethrown as unchecked. | |
161 | * | |
162 | * @param closeable the closeable to be consumed and closed | |
163 | * @param consume the consumer of the {@link AutoCloseable} | |
164 | * | |
165 | * @param <C> the auto-closeable type | |
166 | */ | |
167 | public static <C extends AutoCloseable> void closeAfterAccepting(C closeable, CheckedConsumer<C, ?> consume) | |
168 | { | |
169 | try(C autoClose = closeable) { | |
170 |
1
1. closeAfterAccepting : removed call to io/earcam/unexceptional/Exceptional::accept → KILLED |
Exceptional.accept(consume, autoClose); |
171 | } catch(Exception e) { | |
172 | throw uncheck(e); | |
173 | } | |
174 | } | |
175 | ||
176 | ||
177 | public static <C extends AutoCloseable, T, U> void closeAfterAccepting(CheckedFunction<T, C, ?> create, T t, U instance, CheckedBiConsumer<C, U, ?> consume) | |
178 | { | |
179 | C closeable = Exceptional.apply(create, t); | |
180 |
1
1. closeAfterAccepting : removed call to io/earcam/unexceptional/Closing::closeAfterAccepting → KILLED |
closeAfterAccepting(closeable, instance, consume); |
181 | } | |
182 | ||
183 | ||
184 | /** | |
185 | * Consumes both the {@code closeable} and {@code instance} before closing. Checked exceptions are rethrown as | |
186 | * unchecked. | |
187 | * | |
188 | * @param closeable the closeable to be consumed and closed | |
189 | * @param instance the instance to consume | |
190 | * @param consume the consumer of the {@link AutoCloseable} | |
191 | * | |
192 | * @param <C> the auto-closeable type to consume | |
193 | * @param <U> the type of consumer's second argument | |
194 | */ | |
195 | public static <C extends AutoCloseable, U> void closeAfterAccepting(C closeable, U instance, CheckedBiConsumer<C, U, ?> consume) | |
196 | { | |
197 | try(C autoClose = closeable) { | |
198 |
1
1. closeAfterAccepting : removed call to io/earcam/unexceptional/Exceptional::accept → KILLED |
Exceptional.accept(consume, autoClose, instance); |
199 | } catch(Exception e) { | |
200 | throw uncheck(e); | |
201 | } | |
202 | } | |
203 | ||
204 | public static class AutoClosed<T, E extends Exception> implements AutoCloseable, Supplier<T> { | |
205 | ||
206 | private final T instance; | |
207 | private final CheckedConsumer<T, E> closeMethod; | |
208 | ||
209 | ||
210 | AutoClosed(T instance, CheckedConsumer<T, E> closeMethod) | |
211 | { | |
212 | this.instance = instance; | |
213 | this.closeMethod = closeMethod; | |
214 | } | |
215 | ||
216 | ||
217 | @Override | |
218 | public T get() | |
219 | { | |
220 |
1
1. get : mutated return of Object value for io/earcam/unexceptional/Closing$AutoClosed::get to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return instance; |
221 | } | |
222 | ||
223 | ||
224 | @Override | |
225 | public void close() throws E | |
226 | { | |
227 |
1
1. close : removed call to io/earcam/unexceptional/CheckedConsumer::accept → KILLED |
closeMethod.accept(instance); |
228 | } | |
229 | } | |
230 | ||
231 | ||
232 | /** | |
233 | * Reshape instances to fit try-catch autoclose. | |
234 | * | |
235 | * Example usage: | |
236 | * <code> | |
237 | * try(AutoClosed<VirtualMachine, IOException> vm = Closing.autoClosing(getCurrentVm(), VirtualMachine::detach)) { | |
238 | * vm.get().loadAgent(x, y); | |
239 | * } | |
240 | * </code> | |
241 | * | |
242 | * @param instance | |
243 | * @param closeMethod | |
244 | * @return | |
245 | * | |
246 | * @since 0.5.0 | |
247 | */ | |
248 | public static <T, E extends Exception> AutoClosed<T, E> autoClosing(T instance, CheckedConsumer<T, E> closeMethod) | |
249 | { | |
250 |
1
1. autoClosing : mutated return of Object value for io/earcam/unexceptional/Closing::autoClosing to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return new AutoClosed<>(instance, closeMethod); |
251 | } | |
252 | } | |
Mutations | ||
90 |
1.1 |
|
109 |
1.1 |
|
133 |
1.1 |
|
155 |
1.1 |
|
170 |
1.1 |
|
180 |
1.1 |
|
198 |
1.1 |
|
220 |
1.1 |
|
227 |
1.1 |
|
250 |
1.1 |