/* * This file is part of NucleusFramework for Bukkit, licensed under the MIT License (MIT). * * Copyright (c) JCThePants (www.jcwhatever.com) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package com.jcwhatever.nucleus.utils.observer.future; import com.jcwhatever.nucleus.utils.PreCon; import com.jcwhatever.nucleus.utils.observer.ISubscriber; import com.jcwhatever.nucleus.utils.observer.SubscriberAgent; import com.jcwhatever.nucleus.utils.observer.update.NamedUpdateAgents; import javax.annotation.Nullable; import java.util.ArrayList; import java.util.List; /** * Used for returning asynchronous results from a method. * * @see FutureResultSubscriber * @see IFutureResult */ public class FutureResultAgent<R> extends SubscriberAgent { private final FutureResult<R> _future; private final NamedUpdateAgents _updateAgents = new NamedUpdateAgents(); private Result<R> _finalResult; private boolean _hasFutureSubscribers; /** * Create a new agent and immediately create a success result. * * @param <T> The result type. * * @return The future result. */ public static <T> IFutureResult<T> successResult() { return new FutureResultAgent<T>().success(); } /** * Create a new agent and immediately create a success result. * * @param result The result. * * @param <T> The result type. * * @return The future result. */ public static <T> IFutureResult<T> successResult(@Nullable T result) { return new FutureResultAgent<T>().success(result); } /** * Create a new agent and immediately create a success result. * * @param result The result. * @param message The result message. * @param args Optional message format arguments. * * @param <T> The result type. * * @return The future result. */ public static <T> IFutureResult<T> successResult(@Nullable T result, CharSequence message, Object... args) { return new FutureResultAgent<T>().success(result, message, args); } /** * Create a new agent and immediately create a cancel result. * * @param <T> The result type. * * @return The future result. */ public static <T> IFutureResult<T> cancelResult() { return new FutureResultAgent<T>().cancel(); } /** * Create a new agent and immediately create a cancel result. * * @param result The result. * * @param <T> The result type. * * @return The future result. */ public static <T> IFutureResult<T> cancelResult(@Nullable T result) { return new FutureResultAgent<T>().cancel(result); } /** * Create a new agent and immediately create a cancel result. * * @param result The result. * @param message The result message. * @param args Optional message format arguments. * * @param <T> The result type. * * @return The future result. */ public static <T> IFutureResult<T> cancelResult(@Nullable T result, CharSequence message, Object... args) { return new FutureResultAgent<T>().cancel(result, message, args); } /** * Create a new agent and immediately create an error result. * * @param <T> The result type. * * @return The future result. */ public static <T> IFutureResult<T> errorResult() { return new FutureResultAgent<T>().error(); } /** * Create a new agent and immediately create an error result. * * @param result The result. * * @param <T> The result type. * * @return The future result. */ public static <T> IFutureResult<T> errorResult(@Nullable T result) { return new FutureResultAgent<T>().error(result); } /** * Create a new agent and immediately create an error result. * * @param result The result. * @param message The result message. * @param args Optional message format arguments. * * @param <T> The result type. * * @return The future result. */ public static <T> IFutureResult<T> errorResult(@Nullable T result, CharSequence message, Object... args) { return new FutureResultAgent<T>().error(result, message, args); } /** * Constructor. */ public FutureResultAgent() { _future = new FutureResult<>(this); } @Override public boolean hasSubscribers() { return _hasFutureSubscribers || super.hasSubscribers(); } /** * Send result to subscribers. * * @param result The result. */ public void sendResult(Result<R> result) { PreCon.notNull(result); _finalResult = result; if (!hasSubscribers()) return; if (result.isSuccess()) { sendSuccess(result); } else if (result.isComplete()) { if (result.isCancelled()) { sendCancel(result); } else { sendError(result); } } else { List<ISubscriber> list = new ArrayList<>(subscribers()); for (ISubscriber subscriber : list) { if (subscriber instanceof FutureResultSubscriber) { //noinspection unchecked ((FutureResultSubscriber<R>) subscriber).on(result); } } _updateAgents.update("onResult", result); } } /** * Declare the result cancelled. * * <p>The same as invoking {@link #sendResult} with a generic * cancel result.</p> * * <p>The result object and message are null.</p> * * @return The agents future. */ public FutureResult<R> cancel() { return cancel(null, null); } /** * Declare the result cancelled. * * <p>The same as invoking {@link #sendResult} with a generic * cancel result.</p> * * <p>The result message is null.</p> * * @param result The result object. * * @return The agents future. */ public FutureResult<R> cancel(@Nullable R result) { return cancel(result, null); } /** * Declare the result cancelled. * * <p>The same as calling {@link #sendResult} with a generic * cancel result.</p> * * @param result The result object. * @param message The message to send with the result. * @param args Optional message format arguments. * * @return The agents future. */ public FutureResult<R> cancel(@Nullable R result, @Nullable CharSequence message, Object... args) { sendResult(new ResultBuilder<R>() .cancel() .result(result) .message(message, args) .build()); return getFuture(); } /** * Declare an error in the result. * * <p>The same as invoking {@link #sendResult} with a generic * error result.</p> * * <p>The result object and message are null.</p> * * @return The agents future. */ public FutureResult<R> error() { return error(null, null); } /** * Declare an error in the result. * * <p>The same as invoking {@link #sendResult} with a generic * error result.</p> * * <p>The result message is null.</p> * * @param result The result object. * * @return The agents future. */ public FutureResult<R> error(@Nullable R result) { return error(result, null); } /** * Declare an error in the result. * * <p>The same as invoking {@link #sendResult} with a generic * error result.</p> * * @param result The result object. * @param message The message to send with the result. * @param args Optional message format arguments. * * @return The agents future. */ public FutureResult<R> error(@Nullable R result, @Nullable CharSequence message, Object... args) { sendResult(new ResultBuilder<R>() .error() .result(result) .message(message, args) .build()); return getFuture(); } /** * Declare the result a success. * * <p>The same as invoking {@link #sendResult} with a generic * success result.</p> * * <p>The result object and message are null.</p> * * @return The agents future. */ public FutureResult<R> success() { return success(null, null); } /** * Declare the result a success. * * <p>The same as invoking {@link #sendResult} with a generic * success result.</p> * * <p>The result message is null.</p> * * @param result The result object. * * @return The agents future. */ public FutureResult<R> success(@Nullable R result) { return success(result, null); } /** * Declare the result a success. * * <p>The same as invoking {@link #sendResult} with a generic * success result.</p> * * @param result The result object. * @param message The message to send with the result. * @param args Optional message format arguments. * * @return The agents future. */ public FutureResult<R> success(@Nullable R result, @Nullable CharSequence message, Object... args) { sendResult(new ResultBuilder<R>() .success() .result(result) .message(message, args) .build()); return getFuture(); } /** * Get a future that can be returned so that a method caller can * attach {@link com.jcwhatever.nucleus.utils.observer.update.IUpdateSubscriber}'s. */ public FutureResult<R> getFuture() { return _future; } protected void sendSuccess(Result<R> result) { _updateAgents.update("onSuccess", result); List<ISubscriber> list = new ArrayList<>(subscribers()); for (ISubscriber subscriber : list) { if (subscriber instanceof FutureResultSubscriber) { //noinspection unchecked ((FutureResultSubscriber<R>) subscriber).onSuccess(result); //noinspection unchecked ((FutureResultSubscriber<R>) subscriber).on(result); } } } protected void sendError(Result<R> result) { _updateAgents.update("onError", result); List<ISubscriber> list = new ArrayList<>(subscribers()); for (ISubscriber subscriber : list) { if (subscriber instanceof FutureResultSubscriber) { //noinspection unchecked ((FutureResultSubscriber<R>) subscriber).onError(result); //noinspection unchecked ((FutureResultSubscriber<R>) subscriber).on(result); } } } protected void sendCancel(Result<R> result) { _updateAgents.update("onCancel", result); List<ISubscriber> list = new ArrayList<>(subscribers()); for (ISubscriber subscriber : list) { if (subscriber instanceof FutureResultSubscriber) { //noinspection unchecked ((FutureResultSubscriber<R>) subscriber).onCancel(result); //noinspection unchecked ((FutureResultSubscriber<R>) subscriber).on(result); } } } private static class FutureResult<R> implements IFutureResult<R> { FutureResultAgent<R> parent; private FutureResult(FutureResultAgent<R> parent) { this.parent = parent; } @Override public FutureResult<R> onResult(FutureResultSubscriber<R> subscriber) { PreCon.notNull(subscriber); if (parent._finalResult != null) subscriber.on(parent._finalResult); parent.addSubscriber(subscriber); parent._hasFutureSubscribers = true; return this; } @Override public FutureResult<R> onSuccess(FutureResultSubscriber<R> subscriber) { PreCon.notNull(subscriber); addSubscriber("onSuccess", subscriber); if (parent._finalResult != null && parent._finalResult.isSuccess()) { subscriber.onSuccess(parent._finalResult); subscriber.on(parent._finalResult); } return this; } @Override public FutureResult<R> onCancel(FutureResultSubscriber<R> subscriber) { PreCon.notNull(subscriber); addSubscriber("onCancel", subscriber); if (parent._finalResult != null && parent._finalResult.isCancelled()) { subscriber.onCancel(parent._finalResult); subscriber.on(parent._finalResult); } return this; } @Override public FutureResult<R> onError(FutureResultSubscriber<R> subscriber) { PreCon.notNull(subscriber); addSubscriber("onError", subscriber); if (parent._finalResult != null && !parent._finalResult.isSuccess() && !parent._finalResult.isCancelled()) { subscriber.onError(parent._finalResult); subscriber.on(parent._finalResult); } return this; } private void addSubscriber(String agentName, FutureResultSubscriber<R> subscriber) { parent._updateAgents.getAgent(agentName).addSubscriber(subscriber); parent._hasFutureSubscribers = true; } } }