Closing.java

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
 * &nbsp;&nbsp;&nbsp;<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
Location : closeAfterApplying
Killed by : io.earcam.unexceptional.ClosingTest.closesAfterCreatingThenApplying()
mutated return of Object value for io/earcam/unexceptional/Closing::closeAfterApplying to ( if (x != null) null else throw new RuntimeException ) → KILLED

109

1.1
Location : closeAfterApplying
Killed by : io.earcam.unexceptional.ClosingTest.closesAfterApplyingNull()
mutated return of Object value for io/earcam/unexceptional/Closing::closeAfterApplying to ( if (x != null) null else throw new RuntimeException ) → KILLED

133

1.1
Location : closeAfterApplying
Killed by : io.earcam.unexceptional.ClosingTest.closesAfterBiApplyingNull()
mutated return of Object value for io/earcam/unexceptional/Closing::closeAfterApplying to ( if (x != null) null else throw new RuntimeException ) → KILLED

155

1.1
Location : closeAfterAccepting
Killed by : io.earcam.unexceptional.ClosingTest.closesAfterCreatingThenAccepting()
removed call to io/earcam/unexceptional/Closing::closeAfterAccepting → KILLED

170

1.1
Location : closeAfterAccepting
Killed by : io.earcam.unexceptional.ClosingTest.closesAfterCreatingThenAccepting()
removed call to io/earcam/unexceptional/Exceptional::accept → KILLED

180

1.1
Location : closeAfterAccepting
Killed by : io.earcam.unexceptional.ClosingTest.closesAfterCreatingThenBiAccepting()
removed call to io/earcam/unexceptional/Closing::closeAfterAccepting → KILLED

198

1.1
Location : closeAfterAccepting
Killed by : io.earcam.unexceptional.ClosingTest.closesAfterCreatingThenBiAccepting()
removed call to io/earcam/unexceptional/Exceptional::accept → KILLED

220

1.1
Location : get
Killed by : io.earcam.unexceptional.ClosingTest.autoClosingInstanceAvailable()
mutated return of Object value for io/earcam/unexceptional/Closing$AutoClosed::get to ( if (x != null) null else throw new RuntimeException ) → KILLED

227

1.1
Location : close
Killed by : io.earcam.unexceptional.ClosingTest.autoClosingInvokedImplicitlyByTryWithStatementOnError()
removed call to io/earcam/unexceptional/CheckedConsumer::accept → KILLED

250

1.1
Location : autoClosing
Killed by : io.earcam.unexceptional.ClosingTest.autoClosingInstanceAvailable()
mutated return of Object value for io/earcam/unexceptional/Closing::autoClosing to ( if (x != null) null else throw new RuntimeException ) → KILLED

Active mutators

Tests examined


Report generated by PIT 1.4.0