package io.futuristic;
import io.futuristic.function.ConsumerWithException;
import io.futuristic.function.ExceptionTrapper;
import io.futuristic.function.FunctionWithException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
/**
* @autor: julio
*/
public final class FutureWithTrigger<T> {
private final CallbackLink<T> callbackLink;
private final Callback<T> triggerCallback;
private final Future<T> future;
FutureWithTrigger() {
this.callbackLink = new CallbackLink<>();
this.triggerCallback = this.callbackLink.getFrom();
this.future = createFuture();
}
public Callback<T> getTrigger(){
return this.triggerCallback;
}
public Future<T> getFuture(){
return this.future;
}
private Future<T> createFuture(){
return new Future<T>() {
@Override
public T await() throws Exception{
final CountDownLatch latch = new CountDownLatch(1);
final AtomicReference<T> resultReference = new AtomicReference<>();
final AtomicReference<Exception> errorReference = new AtomicReference<>();
FutureWithTrigger.this.callbackLink.addTo(new Callback<T>() {
@Override
public void completed(T result) {
resultReference.set(result);
latch.countDown();
}
@Override
public void failed(Exception throwable) {
errorReference.set(throwable);
latch.countDown();
}
});
latch.await();
if(errorReference.get() == null){
return resultReference.get();
} else {
throw errorReference.get();
}
}
@Override
public Future<T> consume(ConsumerWithException<T> consumer) {
FutureWithTrigger<T> nextFuture = new FutureWithTrigger<>();
FutureWithTrigger.this.callbackLink.addTo(new Callback<T>() {
@Override
public void completed(T result) {
try {
consumer.accept(result);
} catch (Exception e) {
this.failed(e);
return;
}
nextFuture.getTrigger().completed(result);
}
@Override
public void failed(Exception throwable) {
nextFuture.getTrigger().failed(throwable);
}
});
return nextFuture.getFuture();
}
@Override
public <R> Future<R> map(FunctionWithException<T, R> mapper) {
return mapFuture(t -> new FutureWithValue<R>(mapper.apply(t)));
}
@Override
public <R> Future<R> mapFuture(FunctionWithException<T, Future<R>> mapper) {
FutureWithTrigger<R> nextFuture = new FutureWithTrigger<>();
FutureWithTrigger.this.callbackLink.addTo(new Callback<T>() {
@Override
public void completed(T result) {
try {
Future<R> mapped = mapper.apply(result);
mapped.consume(nextFuture.getTrigger());
} catch (Exception ex) {
failed(ex);
}
}
@Override
public void failed(Exception throwable) {
nextFuture.getTrigger().failed(throwable);
}
});
return nextFuture.getFuture();
}
@Override
public <E extends Exception> Future<T> trap(Class<E> exceptionClass, ExceptionTrapper<E, T> trapper) {
return trapFuture(exceptionClass, e -> new FutureWithValue<>(trapper.trap(e)));
}
@Override
public <E extends Exception> Future<T> trapFuture(Class<E> exceptionClass, ExceptionTrapper<E, Future<T>> trapper) {
FutureWithTrigger<T> nextFuture = new FutureWithTrigger<>();
FutureWithTrigger.this.callbackLink.addTo(new Callback<T>() {
@Override
public void completed(T result) {
}
@Override
public void failed(Exception throwable) {
if (exceptionClass.isAssignableFrom(throwable.getClass())) {
try {
Future<T> res = trapper.trap((E) throwable);
res.consume(nextFuture.getTrigger());
} catch (Exception ex) {
nextFuture.getTrigger().failed(throwable);
}
} else {
nextFuture.getTrigger().failed(throwable);
}
}
});
return nextFuture.getFuture();
}
};
}
}