package com.novoda.event; import com.google.auto.value.AutoValue; import com.google.common.base.Function; import com.google.common.base.Optional; import com.google.common.base.Predicate; import com.google.common.base.Predicates; @AutoValue public abstract class Event<T> { public static <T> Event<T> idle() { return Event.<T>builder() .state(Status.IDLE) .build(); } public static <T> Event<T> idle(Predicate<T> dataValidator) { return Event.<T>builder() .state(Status.IDLE) .dataValidator(dataValidator) .build(); } public static <T> Event<T> loading() { return Event.<T>builder() .state(Status.LOADING) .build(); } public static <T> Event<T> loading(Predicate<T> dataValidator) { return Event.<T>builder() .state(Status.LOADING) .dataValidator(dataValidator) .build(); } public static <T> Event<T> error(Throwable throwable) { return Event.<T>builder() .state(Status.ERROR) .error(Optional.of(throwable)) .build(); } public static <T> Builder<T> builder() { return new AutoValue_Event.Builder<T>() .data(Optional.<T>absent()) .error(Optional.<Throwable>absent()) .dataValidator(Predicates.<T>alwaysTrue()); } Event() { // AutoValue best practices https://github.com/google/auto/blob/master/value/userguide/practices.md } public abstract Status state(); public abstract Optional<T> data(); public abstract Optional<Throwable> error(); protected abstract Predicate<T> dataValidator(); public Optional<String> errorMessage() { return error().transform( new Function<Throwable, String>() { @Override public String apply(Throwable input) { if (input.getMessage() == null) { return "Something went wrong"; } return input.getMessage(); } } ); } public abstract Builder<T> toBuilder(); public Event<T> asError(Throwable error) { return toBuilder() .state(Status.ERROR) .error(Optional.fromNullable(error)) .build(); } public Event<T> asIdle() { return toBuilder() .state(Status.IDLE) .error(Optional.<Throwable>absent()) .build(); } public Event<T> updateData(T value) { return toBuilder() .data(validateData(value)) .build(); } public Event<T> updateData(Optional<T> data) { return toBuilder() .data(data.transform(validateData()).or(Optional.<T>absent())) .build(); } private Function<T, Optional<T>> validateData() { return new Function<T, Optional<T>>() { @Override public Optional<T> apply(T input) { return validateData(input); } }; } public Event<T> removeData() { return toBuilder() .data(Optional.<T>absent()) .build(); } public Event<T> asLoading() { return toBuilder() .state(Status.LOADING) .error(Optional.<Throwable>absent()) .build(); } public Event<T> asLoadingWithData(T value) { return toBuilder() .state(Status.LOADING) .data(validateData(value)) .error(Optional.<Throwable>absent()) .build(); } private Optional<T> validateData(T value) { if (dataValidator().apply(value)) { return Optional.of(value); } else { return Optional.absent(); } } @AutoValue.Builder public abstract static class Builder<T> { public abstract Builder<T> state(Status state); public abstract Builder<T> data(Optional<T> data); public abstract Builder<T> error(Optional<Throwable> error); public abstract Builder<T> dataValidator(Predicate<T> dataValidator); public abstract Event<T> build(); } }