package no.java.incogito.application; import fj.Bottom; import fj.Effect; import fj.F; import fj.P1; import fj.Unit; import fj.data.Option; import fj.data.Either; import static fj.data.Option.some; /** * @author <a href="mailto:trygve.laugstol@arktekk.no">Trygve Laugstøl</a> * @version $Id$ */ public abstract class OperationResult<T> { enum Status { OK, CONFLICT, NOT_FOUND } public final Status status; private OperationResult(Status status) { this.status = status; } public boolean isOk() { return this instanceof OkOperationResult; } public boolean isConflict() { return this instanceof ConflictOperationResult; } public boolean isNotFound() { return this instanceof NotFoundOperationResult; } public abstract T value(); public boolean hasValue() { return false; } // ----------------------------------------------------------------------- // Data Constructors // ----------------------------------------------------------------------- public static class OkOperationResult<T> extends OperationResult<T> { public final Option<T> data; private OkOperationResult(Option<T> data) { super(Status.OK); this.data = data; } public T value() { return data.some(); } public boolean hasValue() { return data.isSome(); } } public static <T> OperationResult<T> ok(T t) { return new OkOperationResult<T>(some(t)); } public static <T> OperationResult<T> fromOption(Option<T> t, String noneMessage) { return t.isSome() ? ok(t.some()) : OperationResult.<T>notFound(noneMessage); } public static <T> OperationResult<T> fromEither(Either<String, T> x) { return x.either(OperationResult.<T>notFound_(), OperationResult.<T>ok_()); } public static OperationResult<Unit> emptyOk() { return new OkOperationResult<Unit>(Option.<Unit>none()); } public static <T> F<T, OperationResult<T>> ok_() { return new F<T, OperationResult<T>>() { public OperationResult<T> f(T t) { return ok(t); } }; } public static <T> F<Option<T>, OperationResult<T>> fromOption_(final String noneMessage) { return new F<Option<T>, OperationResult<T>>() { public OperationResult<T> f(Option<T> t) { return fromOption(t, noneMessage); } }; } public static <T> P1<OperationResult<T>> $ok(final T t) { return new P1<OperationResult<T>>() { @Override public OperationResult<T> _1() { return ok(t); } }; } // Conflict public static class ConflictOperationResult<T> extends OperationResult<T> { public final String message; private ConflictOperationResult(String message) { super(Status.CONFLICT); this.message = message; } public T value() { throw Bottom.error(message); } } public static <T> OperationResult<T> conflict(String message) { return new ConflictOperationResult<T>(message); } public static <T> P1<OperationResult<T>> $conflict(final String message) { return new P1<OperationResult<T>>() { public OperationResult<T> _1() { return conflict(message); } }; } public static <T> F<T, OperationResult<T>> conflict_(final String message) { return new F<T, OperationResult<T>>() { public OperationResult<T> f(T t) { return conflict(message); } }; } // Not Found public static class NotFoundOperationResult<T> extends OperationResult<T> { public final String message; private NotFoundOperationResult(String message) { super(Status.NOT_FOUND); this.message = message; } public T value() { throw Bottom.error(message); } } public static <T> OperationResult<T> notFound(String message) { return new NotFoundOperationResult<T>(message); } public static <T> F<String, OperationResult<T>> notFound_() { return new F<String, OperationResult<T>>() { public OperationResult<T> f(String message) { return notFound(message); } }; } public static <T> P1<OperationResult<T>> $notFound(final String message) { return new P1<OperationResult<T>>() { public OperationResult<T> _1() { return notFound(message); } }; } // ----------------------------------------------------------------------- // Projections // ----------------------------------------------------------------------- public OkProjection ok() { return new OkProjection(); } public class OkProjection { public <B> OperationResult<B> map(F<T, B> f) { if (isOk()) { if (hasValue()) { return new OkOperationResult<B>(some(f.f(value()))); } return new OkOperationResult<B>(Option.<B>none()); } else { //noinspection unchecked return (OperationResult<B>) OperationResult.this; } } public <B> OperationResult<B> bind(F<T, Option<B>> f) { if (isOk()) { if (hasValue()) { return new OkOperationResult<B>(f.f(value())); } return new OkOperationResult<B>(Option.<B>none()); } else { //noinspection unchecked return (OperationResult<B>) OperationResult.this; } } public void foreach(Effect<T> effect) { if (isOk()) { effect.e(value()); } } } // ----------------------------------------------------------------------- // // ----------------------------------------------------------------------- public void foreach(Effect<T> effect) { if(isOk() && hasValue()) { effect.e(value()); } } // ----------------------------------------------------------------------- // Utilities // ----------------------------------------------------------------------- public static <T> OperationResult<T> joinOk(OperationResult<OperationResult<T>> operationResult) { if(operationResult.isOk()) { return operationResult.value(); } //noinspection unchecked return (OperationResult<T>) operationResult; } }