package the8472.utils;
import static java.util.Collections.emptyList;
import static java.util.concurrent.CompletableFuture.completedFuture;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.concurrent.CompletionStage;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
public class Functional {
public static <T> T tap(T obj, Consumer<T> c) {
c.accept(obj);
return obj;
}
public static <T, E extends Throwable> T tapThrow(T obj, ThrowingConsumer<T, E> c) throws E {
c.accept(obj);
return obj;
}
public static interface ThrowingConsumer<T,E extends Throwable> {
void accept(T arg) throws E;
}
@FunctionalInterface
public static interface ThrowingSupplier<T,E extends Throwable> {
T get() throws E;
}
@FunctionalInterface
public static interface ThrowingFunction<R, T, E extends Throwable> {
R apply(T arg) throws E;
}
public static <T> T unchecked(ThrowingSupplier<? extends T, ?> f) {
try {
return f.get();
} catch (Throwable e) {
throwAsUnchecked(e);
return null;
}
}
public static <IN, OUT> OUT sync(IN obj, Function<IN, OUT> supp) {
synchronized(obj) {
return supp.apply(obj);
}
}
public static <RES, T extends AutoCloseable> RES autoclose(Supplier<T> s, Function<T,RES> f) throws Exception {
RES result;
try(T t = s.get()) {
result = f.apply(t);
}
return result;
}
public static <R,T> Function<T,R> unchecked(ThrowingFunction<R,T,?> f) {
return (arg) -> {
try {
return f.apply(arg);
} catch(Throwable e) {
throwAsUnchecked(e);
return null;
}
};
}
public static <T> Consumer<T> uncheckedCons(ThrowingConsumer<T, ?> cons) {
return (arg) -> {
try {
cons.accept(arg);
} catch (Throwable e) {
throwAsUnchecked(e);
return;
}
};
}
public static <T> CompletionStage<List<T>> awaitAll(Collection<? extends CompletionStage<T>> stages) {
return stages.stream().map(st -> st.thenApply(Collections::singletonList)).reduce(completedFuture(emptyList()), (f1, f2) -> f1.thenCombine(f2, (a, b) -> tap(new ArrayList<>(a), l -> l.addAll(b))));
}
public static <IN, OUT, EX extends Throwable> Function<IN, OUT> castOrThrow(Class<OUT> type, Function<IN, EX> ex) {
return (in) -> {
if(!type.isInstance(in))
throwAsUnchecked(ex.apply(in));
return type.cast(in);
};
}
public static void throwAsUnchecked(Throwable t) {
Thrower.asUnchecked(t);
}
private static class Thrower {
@SuppressWarnings("unchecked")
static private <T extends Throwable> void asUnchecked(Throwable t) throws T {
throw (T) t;
}
}
@SuppressWarnings("unchecked") // a supertype of the expected T can be passed to allow type-inference to match erased generics
public static <K, T> Optional<T> typedGet(Map<? super K, ?> map, K key, Class<? super T> clazz) {
return (Optional<T>) Optional.ofNullable(map.get(key)).filter(clazz::isInstance).map(clazz::cast);
}
static class ShortCircuitFlatMapSpliterator<R,T> extends Spliterators.AbstractSpliterator<R> {
Spliterator<T> sourceSpliterator;
Stream<R> currentSubStream;
Spliterator<R> currentSubSpliterator;
Function<T, Stream<R>> flatMapper;
protected ShortCircuitFlatMapSpliterator(Spliterator<T> source, Function<T,Stream<R>> flatMapper) {
super(source.estimateSize(), 0);
sourceSpliterator = source;
this.flatMapper = flatMapper;
}
@Override
public boolean tryAdvance(Consumer<? super R> action) {
for(;;) {
if(currentSubSpliterator == null) {
if(!sourceSpliterator.tryAdvance(t -> {
currentSubStream = flatMapper.apply(t);
}))
return false;
if(currentSubStream == null)
continue;
currentSubSpliterator = currentSubStream.spliterator();
}
if(currentSubSpliterator.tryAdvance(action))
return true;
currentSubSpliterator = null;
currentSubStream.close();
}
}
void close() {
if(currentSubStream != null) {
currentSubStream.close();
}
}
}
/**
* workaround for https://bugs.openjdk.java.net/browse/JDK-8075939
*/
public static <R, T> Stream<R> shortCircuitingflatMap(Stream<T> st, Function<T, Stream<R>> flatMapper) {
Spliterator<T> sourceSpliterator = st.spliterator();
ShortCircuitFlatMapSpliterator<R,T> sinkSpliterator = new ShortCircuitFlatMapSpliterator<>(sourceSpliterator, flatMapper);
Stream<R> resultStream = StreamSupport.stream(sinkSpliterator, false);
resultStream = resultStream.onClose(st::close).onClose(sinkSpliterator::close);
return resultStream;
}
}