/*
This file is part of Reactive Cascade which is released under The MIT License.
See license.md , https://github.com/futurice/cascade and http://reactivecascade.com for details.
This is open source for the common good. Please contribute improvements by pull request or contact paulirotta@gmail.com
*/
package com.reactivecascade.functional;
import android.support.annotation.CallSuper;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.reactivecascade.i.IAction;
import com.reactivecascade.i.IActionOne;
import com.reactivecascade.i.IActionOneR;
import com.reactivecascade.i.IActionR;
import com.reactivecascade.i.IAltFuture;
import com.reactivecascade.i.IBaseAction;
import com.reactivecascade.i.ISafeGettable;
import com.reactivecascade.util.AssertUtil;
import com.reactivecascade.util.RCLog;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicReference;
/**
* This can be useful for referring in a lambda expression to the the lambda expression.
* <p>
* This can also be useful for removing all references to an intermediate from such that is may be
* garbage collected if needed. Intermediate values in a active chain may be consuming
* a lot of memory. In asynchronous functional chains based on RunnableAltFuture, dereference of
* intermediate values when going on to the next function, but only in production builds.
* <p>
* Note that there is no mutator method which allows to re-enter the initial {@link com.reactivecascade.i.IGettable#VALUE_NOT_AVAILABLE}
* state once it is lost. Thus one can not abuse the system to make an immutable mutable by moving back
* through this intermediate state. Once you leave the temple, you can never go back.
* <p>
* Note that <code>null</code> is not a permissible from
* <p>
* This uses a free thread model (any calling thread is used for all chained functions). This is a
* main difference from the similar {@link com.reactivecascade.functional.SettableAltFuture} which forces
* the specified evaluation thread group.
*
* @param <T>
*/
//TODO Do we also need an AddOnlyList type which is a collection that can only grow from the end?
//@Deprecated // Delete this and use SettableAltFuture instead for simplicity
public class ImmutableValue<T> implements ISafeGettable<T> {
@SuppressWarnings("unchecked")
private final AtomicReference<T> valueAR = new AtomicReference<>((T) VALUE_NOT_AVAILABLE);
private final ConcurrentLinkedQueue<IBaseAction<T>> thenActions = new ConcurrentLinkedQueue<>();
@Nullable
private final IActionR<T> action;
/**
* Create but do not yet set the from of an underlying {@link java.util.concurrent.atomic.AtomicReference}
* <p>
* You can use this object as a placeholder until an asynchronous function result is finally set.
* <p>
* You can also attach code which will run when the from is set. See {@link #then(IAction)}
* and {@link #then(IActionOne)}
*/
public ImmutableValue() {
action = null;
}
/**
* This constructor creates and initialized the from to its final from.
* <p>
* It can be useful to create for example default values.
*
* @param value the value to immediately set
*/
public ImmutableValue(@NonNull T value) {
set(value);
action = null;
}
public ImmutableValue(@Nullable IActionR<T> action) {
this.action = action;
}
/**
* Atomic update.
* <p>
* See {@link java.util.concurrent.atomic.AtomicReference#compareAndSet(Object, Object)} for
* a description and use examples of this pattern.
*
* @param expected value
* @param value to set if expected is the current value
* @return if <code>false</code> is returned, you may try again as the from as needed since the
* final from has not been set.
*/
private boolean compareAndSet(@NonNull T expected,
@NonNull T value) {
boolean success = valueAR.compareAndSet(expected, value);
if (success) {
doThenActions(value);
} else {
RCLog.d(this, "compareAndSet(" + expected + ", " + value + ") failed, current from is " + safeGet());
}
return success;
}
/**
* Add an action which will be run when {@link #set(Object)} is called.
* <p>
* If the from is already {@link #set(Object)}, the passed mOnFireAction will be run immediately and
* synchronously.
*
* @return this
*/
@NonNull
public ImmutableValue<T> then(@NonNull IActionOne<T> action) {
thenActions.add(action);
if (isSet()) {
doThenActions(safeGet());
}
return this;
}
/**
* Add an action which will be run when {@link #set(Object)} is called.
* <p>
* If the from is already {@link #set(Object)}, the passed mOnFireAction will be run immediately and
* synchronously.
*
* @return this
*/
@NonNull
public ImmutableValue<T> then(@NonNull IAction<T> action) {
thenActions.add(action);
if (isSet()) {
doThenActions(safeGet());
}
return this;
}
private void doThenActions(@NonNull T value) {
for (IBaseAction<T> action : thenActions) {
if (thenActions.remove(action)) {
try {
call(value, action);
} catch (Exception e) {
RCLog.e(this, "Can not do then() actions after ImmutableValue was set to from=" + value, e);
}
}
}
}
@Nullable
@SuppressWarnings("unchecked")
// IN->OUT must be bent to match all cases but the context makes this safe
private <IN, OUT> OUT call(@NonNull final IN in,
@NonNull final IBaseAction<IN> action) throws Exception {
if (action instanceof IAction) {
((IAction) action).call();
return null;
} else if (action instanceof IActionOne) {
((IActionOne<IN>) action).call(in);
return null;
} else if (action instanceof IActionOneR) {
return ((IActionOneR<IN, OUT>) action).call(in);
} else if (action instanceof IActionR) {
return ((IActionR<OUT>) action).call();
}
throw new UnsupportedOperationException("Not sure how to call this IBaseAction type: " + action.getClass());
}
/**
* Check if the immutable from has been asserted yet.
* <p>
* Note that if you think you need this for your core logic, you may want to ask yourself if you can
* be better served by using the dependency mechanism of {@link RunnableAltFuture#then(IAltFuture)}
* and similar calls. It is often better to let the preconditions for a {@link #get()} be set by the functional
* chain. If still needed, you may inserting your own atomic or non-atomic logic such as
* <pre>
* <code>
* if (!immutableValue.isSet()) {
* immutableValue.set(from); // Not thread save. This will throw an IllegalStateException if someone else set the from between the previous line and this line
* }
* </code>
* </pre>
*
* @return <code>true</code> if the value is already asserted
*/
public final boolean isSet() {
return valueAR.get() != VALUE_NOT_AVAILABLE;
}
/**
* Get the from, or throw {@link java.lang.IllegalStateException} if you are getting the from
* before it has been set.
* <p>
* Generally you want to use this method instead of {@link #safeGet()} when you can use dependency
* mechanisms properly. If you think have problems, ask if you should be doing some of your logic
* in a <code>.subscribe()</code> clause to guarantee the execution order.
*
* @return
* @throws IllegalStateException if the supplied lazy evaluation IAction throws an error during evaluation
*/
@CallSuper
@NonNull
@SuppressWarnings("unchecked")
public T get() {
T value = valueAR.get();
if (value == VALUE_NOT_AVAILABLE) {
try {
if (action != null) {
T t = action.call();
if (!compareAndSet((T) VALUE_NOT_AVAILABLE, t)) {
RCLog.d(this, "ImmutableValue was set while calling action during get: ignoring the second value from action \"" + t + "\" in favor of \"" + valueAR.get() + "\"");
}
return t;
}
throw new IllegalStateException("Null action and from has not been set()");
} catch (Exception e) {
throw new IllegalStateException("Can not evaluate the supplied ImmutableValue.IAction", e);
}
}
return value;
}
/**
* Get the from, or return <code>null</code> if the from is not yet set.
* <p>
* Usually you will have a better design with {@link #get()} instead. This is fine to use
* if other parts of your application is reactive and will update the from again when it has
* been finally set.
*
* @return the from, or <code>null</code> if the from has not yet been determined
*/
@CallSuper
@SuppressWarnings("unchecked")
@NonNull
public T safeGet() {
return valueAR.get();
}
/**
* Set the from.
* <p>
* If this atomic mOnFireAction succeeds, any {@link #then(IActionOne)} actions
* will also be run synchronously. This is a fairly low-level class which is used by other classes
* and for practical reasons it violates the "always asynchronous" assumption. Traditional. Sorry. :)
*
* @param value
* @return
*/
@CallSuper
@NonNull
@SuppressWarnings("unchecked")
public T set(@NonNull T value) {
AssertUtil.assertNotNull(value);
if (!compareAndSet((T) VALUE_NOT_AVAILABLE, value)) {
throw new IllegalStateException("ImmutableReference can not be set multiple times. It is already set to " + safeGet() + " so we can not assert new from=" + value);
}
doThenActions(value);
return value;
}
/**
* Return the current from of the immutable from if possible
* <p>
* In other cases marker text "(ImmutableValue not yet set)" will be returned. If you see
* this text, consider using a {@link #then(IActionOne)} mOnFireAction
* to make your logic run when this from is set.
*
* @return the string representation, or "Value not available" if not yet determined and can not be determined by an on-get action
*/
@CallSuper
@NonNull
public String toString() {
return safeGet().toString();
}
}