/*******************************************************************************
* Copyright (c) 2016 vogella GmbH and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Simon Scholz <simon.scholz@vogella.com> - initial API and implementation
******************************************************************************/
package org.eclipse.core.databinding.observable.sideeffect;
import java.util.function.Consumer;
import java.util.function.Supplier;
import org.eclipse.core.databinding.observable.Realm;
import org.eclipse.core.internal.databinding.observable.sideeffect.SideEffectFactory;
/**
* <p>
* A factory to create {@link ISideEffect} objects, which are applied to the
* given {@link Consumer} in {@link ISideEffectFactory#createFactory(Consumer)}.
* </p>
* Callers who provide the Consumer<ISideEffect> for the creation of an
* {@link ISideEffectFactory}, are supposed to manage the lifecycle of the
* aggregated {@link ISideEffect} instances, which are created by this factory.
*
* @see SideEffectFactory
* @see CompositeSideEffect
*
* @since 1.6
* @noimplement This interface is not intended to be implemented by clients.
*/
public interface ISideEffectFactory {
/**
* Creates a new {@link ISideEffectFactory} which will notify the given
* {@link Consumer} of every {@link ISideEffect} that is constructed by the
* factory.
* <p>
* For example, a {@link Consumer} could be passed to this method which
* automatically inserts every {@link ISideEffect} into the same
* {@link CompositeSideEffect}, allowing their lifecycle to be managed
* automatically by the object which provides the factory.
* <p>
* Callers who invoke this {@link #createFactory(Consumer)} method are
* supposed to manage the lifecycle of the aggregated {@link ISideEffect}
* instances, which are created by this factory. They do so by passing in a
* consumer which collects the side-effects constructed by the factory,
* allowing {@link ISideEffect#dispose()} to be invoked on them at a later
* time.
*
* @param sideEffectConsumer
* a consumer which will be notified about every
* {@link ISideEffect} constructed by this factory. The consumer
* must guarantee that {@link ISideEffect#dispose()} will be
* called on every {@link ISideEffect} it receives at some point
* in the future.
* @return a newly constructed {@link ISideEffectFactory}
* @see CompositeSideEffect
*/
static ISideEffectFactory createFactory(Consumer<ISideEffect> sideEffectConsumer) {
return new SideEffectFactory(sideEffectConsumer);
}
/**
* Creates a new {@link ISideEffect} on the default {@link Realm} but does
* not run it immediately. The lifecycle of the returned {@link ISideEffect}
* will be managed by the factory.
* <p>
* Callers are not required to dispose the resulting {@link ISideEffect},
* but may do so if they wish to dispose it early.
* <p>
* This is similar to {@link ISideEffect#createPaused(Runnable)} except that
* the resulting {@link ISideEffect} will have its lifecycle managed by the
* factory.
*
* @param runnable
* the runnable to execute. Must be idempotent.
* @return a newly-created {@link ISideEffect} which has not yet been
* activated. Callers may call {@link ISideEffect#dispose()} on the
* result if they wish to dispose it early, but they are not
* required to do so since the lifecycle of the returned
* {@link ISideEffect} is managed automatically.
*/
ISideEffect createPaused(Runnable runnable);
/**
* Creates a new {@link ISideEffect} on the given Realm but does not
* activate it immediately. Callers are not required to dispose the
* resulting {@link ISideEffect}, but may do so if they wish to dispose it
* early.
* <p>
* This is similar to {@link ISideEffect#createPaused(Realm, Runnable)}
* except that the resulting {@link ISideEffect} will have its lifecycle
* managed by the factory.
*
* @param realm
* the realm to execute
* @param runnable
* the runnable to execute. Must be idempotent.
* @return a newly-created {@link ISideEffect} which has not yet been
* activated. Callers may call {@link ISideEffect#dispose()} on the
* result if they wish to dispose it early, but they are not
* required to do so since the lifecycle of the returned
* {@link ISideEffect} is managed automatically.
*/
ISideEffect createPaused(Realm realm, Runnable runnable);
/**
* Runs the given runnable once synchronously. The runnable will then run
* again after any tracked getter invoked by the runnable changes. It will
* continue doing so until the returned {@link ISideEffect} is disposed. The
* returned {@link ISideEffect} is associated with the default realm.
* <p>
* Callers are not required to dispose the resulting {@link ISideEffect},
* but may do so if they wish to dispose it early.
* <p>
* This is similar to {@link ISideEffect#create(Runnable)} except that the
* resulting {@link ISideEffect} will have its lifecycle managed by the
* factory.
*
* @param runnable
* an idempotent runnable which will be executed once
* synchronously then additional times after any tracked getter
* it uses changes state
* @return an {@link ISideEffect} interface that may be used to stop the
* side-effect from running. The {@link Runnable} will not be
* executed anymore after the dispose method is invoked. Callers may
* call {@link ISideEffect#dispose()} on the result if they wish to
* dispose it early, but they are not required to do so since the
* lifecycle of the returned {@link ISideEffect} is managed
* automatically.
*/
ISideEffect create(Runnable runnable);
/**
* Runs the supplier and passes its result to the consumer. The same thing
* will happen again after any tracked getter invoked by the supplier
* changes. It will continue to do so until the given {@link ISideEffect} is
* disposed. The returned {@link ISideEffect} is associated with the default
* realm.
* <p>
* Callers are not required to dispose the resulting {@link ISideEffect},
* but may do so if they wish to dispose it early.
* <p>
* The ISideEffect will initially be in the resumed state.
* <p>
* The first invocation of this method will be synchronous. This version is
* slightly more efficient than {@link #createResumed(Supplier, Consumer)}
* and should be preferred if synchronous execution is acceptable.
* <p>
* This is similar to {@link ISideEffect#create(Supplier, Consumer)} except
* that the resulting {@link ISideEffect} will have its lifecycle managed by
* the factory.
*
* @param supplier
* a supplier which will compute a value and be monitored for
* changes in tracked getters. It should be side-effect-free.
* @param consumer
* a consumer which will receive the value. It should be
* idempotent. It will not be monitored for tracked getters.
*
* @return an {@link ISideEffect} interface that may be used to stop the
* side-effect from running. The {@link Runnable} will not be
* executed anymore after the dispose method is invoked. Callers may
* call {@link ISideEffect#dispose()} on the result if they wish to
* dispose it early, but they are not required to do so since the
* lifecycle of the returned {@link ISideEffect} is managed
* automatically.
*/
<T> ISideEffect create(Supplier<T> supplier, Consumer<T> consumer);
/**
* Runs the supplier and passes its result to the consumer. The same thing
* will happen again after any tracked getter invoked by the supplier
* changes. It will continue to do so until the given {@link ISideEffect} is
* disposed. The returned {@link ISideEffect} is associated with the default
* realm.
* <p>
* Callers are not required to dispose the resulting {@link ISideEffect},
* but may do so if they wish to dispose it early.
* <p>
* The ISideEffect will initially be in the resumed state.
* <p>
* The first invocation of this method will be asynchronous. This is useful,
* for example, when constructing an {@link ISideEffect} in a constructor
* since it ensures that the constructor will run to completion before the
* first invocation of the {@link ISideEffect}. However, this extra safety
* comes with a small performance cost over
* {@link #create(Supplier, Consumer)}.
* <p>
* This is similar to {@link ISideEffect#createResumed(Supplier, Consumer)}
* except that the resulting {@link ISideEffect} will have its lifecycle
* managed by the factory.
*
* @param supplier
* a supplier which will compute a value and be monitored for
* changes in tracked getters. It should be side-effect-free.
* @param consumer
* a consumer which will receive the value. It should be
* idempotent. It will not be monitored for tracked getters.
*
* @return an {@link ISideEffect} interface that may be used to stop the
* side-effect from running. The {@link Runnable} will not be
* executed anymore after the dispose method is invoked. Callers may
* call {@link ISideEffect#dispose()} on the result if they wish to
* dispose it early, but they are not required to do so since the
* lifecycle of the returned {@link ISideEffect} is managed
* automatically.
*/
<T> ISideEffect createResumed(Supplier<T> supplier, Consumer<T> consumer);
/**
* Runs the given supplier until it returns a non-null result. The first
* time it returns a non-null result, that result will be passed to the
* consumer and the ISideEffect will dispose itself. As long as the supplier
* returns null, any tracked getters it invokes will be monitored for
* changes. If they change, the supplier will be run again at some point in
* the future.
* <p>
* The resulting ISideEffect will be dirty and resumed, so the first
* invocation of the supplier will be asynchronous. If the caller needs it
* to be invoked synchronously, they can call
* {@link ISideEffect#runIfDirty()}
* <p>
* Unlike {@link #create(Supplier, Consumer)}, the consumer does not need to
* be idempotent.
* <p>
* This method is used for gathering asynchronous data before opening an
* editor, saving to disk, opening a dialog box, or doing some other
* operation which should only be performed once.
* <p>
* Consider the following example, which displays the content of a text file
* in a message box without doing any file I/O on the UI thread.
* <p>
*
* <pre>
* IObservableValue<String> loadFileAsString(String filename) {
* // Uses another thread to load the given filename. The resulting observable returns
* // null if the file is not yet loaded or contains the file contents if the file is
* // fully loaded
* // ...
* }
*
* void showFileContents(Shell parentShell, String filename) {
* IObservableValue<String> webPageContent = loadFileAsString(filename);
* ISideEffect.runOnce(webPageContent::getValue, (content) -> {
* MessageDialog.openInformation(parentShell, "Your file contains", content);
* })
* }
* </pre>
* This is similar to
* {@link ISideEffect#consumeOnceAsync(Supplier, Consumer)} except that the
* resulting {@link ISideEffect} will have its lifecycle managed by the
* factory.
*
* @param supplier
* supplier which returns null if the side-effect should continue
* to wait or returns a non-null value to be passed to the
* consumer if it is time to invoke the consumer
* @param consumer
* a (possibly non-idempotent) consumer which will receive the
* first non-null result returned by the supplier.
* @return a side-effect which can be used to control this operation. If it
* is disposed before the consumer is invoked, the consumer will
* never be invoked. It will not invoke the supplier if it is
* paused.
*/
<T> ISideEffect consumeOnceAsync(Supplier<T> supplier, Consumer<T> consumer);
}