/**
*
* Copyright (c) 2006-2017, Speedment, Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); You may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.speedment.runtime.core.internal.stream.autoclose;
import com.speedment.runtime.core.exception.SpeedmentException;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.function.*;
import java.util.stream.*;
import static com.speedment.common.invariant.NullUtil.requireNonNullElements;
/**
*
* @author pemi
*/
public abstract class AbstractAutoClosingStream implements AutoCloseable {
private final Set<BaseStream<?, ?>> streamSet;
AbstractAutoClosingStream(Set<BaseStream<?, ?>> streamSet) {
this.streamSet = streamSet;
}
protected Set<BaseStream<?, ?>> getStreamSet() {
return streamSet;
}
protected abstract BaseStream<?, ?> getStream();
@Override
public void close() {
final Set<BaseStream<?, ?>> streamsToClose = new HashSet<>(streamSet); // Copy the set
streamSet.clear(); // Clear the shared streamSet so that other streams will not close again
try {
composedClose(streamsToClose.toArray(new BaseStream<?, ?>[0]));
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
getStream().close(); // Close the underlying stream
}
}
protected <T> boolean finallyClose(BooleanSupplier bs) {
try {
return bs.getAsBoolean();
} finally {
close();
}
}
protected <T> long finallyClose(LongSupplier lp) {
try {
return lp.getAsLong();
} finally {
close();
}
}
protected <T> int finallyClose(IntSupplier is) {
try {
return is.getAsInt();
} finally {
close();
}
}
protected <T> double finallyClose(DoubleSupplier ds) {
try {
return ds.getAsDouble();
} finally {
close();
}
}
protected <T> void finallyClose(Runnable r) {
try {
r.run();
} finally {
close();
}
}
protected <T> T finallyClose(Supplier<T> s) {
try {
return s.get();
} finally {
close();
}
}
public <T> Stream<T> wrap(Stream<T> stream) {
return wrap(stream, getStreamSet(), AutoClosingReferenceStream::new);
}
public IntStream wrap(IntStream stream) {
return wrap(stream, getStreamSet(), AutoClosingIntStream::new);
}
public LongStream wrap(LongStream stream) {
return wrap(stream, getStreamSet(), AutoClosingLongStream::new);
}
public DoubleStream wrap(DoubleStream stream) {
return wrap(stream, getStreamSet(), AutoClosingDoubleStream::new);
}
private <T> T wrap(T stream, Set<BaseStream<?, ?>> streamSet, BiFunction<T, Set<BaseStream<?, ?>>, T> wrapper) {
if (stream instanceof AbstractAutoClosingStream) {
return stream; // If we allready are wrapped, then do not wrap again
}
return wrapper.apply(stream, streamSet);
}
public static UnsupportedOperationException newUnsupportedException(String methodName) {
return new UnsupportedOperationException("The " + methodName + "() method is unsupported because otherwise the AutoClose property cannot be guaranteed");
}
protected static Set<BaseStream<?, ?>> newSet() {
return new HashSet<>();
}
/**
* Given a number of streams, closes the streams in sequence, even if one or
* several throws an Exception. If several throw exceptions, the exceptions
* will be added to the first exception.
*
* @param <T> Stream type
* @param closeables to close
* @throws SpeedmentException if at least one of the close() operations
* throws an exception
*/
@SafeVarargs // Creating a Stream of an array is safe.
@SuppressWarnings({"unchecked", "varargs"})
public static <T extends AutoCloseable> void composedClose(T... closeables) {
requireNonNullElements(closeables);
Exception exception = null;
for (final T closable : closeables) {
try {
closable.close();
} catch (Exception e) {
if (exception == null) {
exception = e;
} else {
try {
exception.addSuppressed(e);
} catch (Exception ignored) {
}
}
}
}
if (exception != null) {
throw new SpeedmentException(exception);
}
}
/**
* Given a number of Runnables, runs the run() method in sequence, even if
* one or several throws an Exception. If several throw exceptions, the
* exceptions will be added to the first exception.
*
* @param runnables to close
* @throws SpeedmentException if at least one of the run() operations throws
* an exception
*/
public static void composedRunnable(Collection<Runnable> runnables) {
requireNonNullElements(runnables);
final AutoCloseable[] closables = new AutoCloseable[runnables.size()];
int i = 0;
for (final Runnable r : runnables) {
closables[i++] = new CloseImpl(r);
}
composedClose(closables);
}
private static class CloseImpl implements AutoCloseable {
private final Runnable r;
public CloseImpl(Runnable r) {
this.r = r;
}
@Override
public void close() {
r.run();
}
}
}