/* * Copyright (C) 2014 The Dagger Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dagger.producers.internal; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.util.concurrent.Futures.catchingAsync; import static com.google.common.util.concurrent.Futures.transform; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; import com.google.common.base.Function; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.util.concurrent.AsyncFunction; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import dagger.producers.Produced; import dagger.producers.Producer; import java.util.List; import java.util.Map; import java.util.Set; import javax.inject.Provider; /** * Utility methods for use in generated producer code. */ public final class Producers { /** * Returns a future of {@link Produced} that represents the completion (either success or failure) * of the given future. If the input future succeeds, then the resulting future also succeeds with * a successful {@code Produced}; if the input future fails, then the resulting future succeeds * with a failing {@code Produced}. * *

Cancelling the resulting future will propagate the cancellation to the input future; but * cancelling the input future will trigger the resulting future to succeed with a failing * {@code Produced}. */ // TODO(beder): Document what happens with an InterruptedException after you figure out how to // trigger one in a test. public static ListenableFuture> createFutureProduced(ListenableFuture future) { return catchingAsync( transform(future, Producers.resultToProduced(), directExecutor()), Throwable.class, Producers.futureFallbackForProduced(), directExecutor()); } private static final Function> RESULT_TO_PRODUCED = new Function>() { @Override public Produced apply(Object result) { return Produced.successful(result); } }; @SuppressWarnings({"unchecked", "rawtypes"}) // bivariant implementation private static Function> resultToProduced() { return (Function) RESULT_TO_PRODUCED; } private static final AsyncFunction> FUTURE_FALLBACK_FOR_PRODUCED = new AsyncFunction>() { @Override public ListenableFuture> apply(Throwable t) throws Exception { Produced produced = Produced.failed(t); return Futures.immediateFuture(produced); } }; @SuppressWarnings({"unchecked", "rawtypes"}) // bivariant implementation private static AsyncFunction> futureFallbackForProduced() { return (AsyncFunction) FUTURE_FALLBACK_FOR_PRODUCED; } /** * Returns a future of a {@code Set} that contains a single element: the result of the input * future. */ public static ListenableFuture> createFutureSingletonSet(ListenableFuture future) { return transform( future, new Function>() { @Override public Set apply(T value) { return ImmutableSet.of(value); } }, directExecutor()); } /** * Creates a new {@code ListenableFuture} whose value is a set containing the values of all its * input futures, if all succeed. If any input fails, the returned future fails immediately. * *

This is the set equivalent of {@link Futures#allAsList}. */ public static ListenableFuture> allAsSet( Iterable> futures) { return transform( Futures.allAsList(futures), new Function, Set>() { @Override public Set apply(List values) { return ImmutableSet.copyOf(values); } }, directExecutor()); } /** * Returns a producer that immediately executes the binding logic for the given provider every * time it is called. */ public static Producer producerFromProvider(final Provider provider) { checkNotNull(provider); return new CompletedProducer() { @Override public ListenableFuture get() { return Futures.immediateFuture(provider.get()); } }; } /** * Returns a producer that succeeds with the given value. * * @deprecated Prefer the non-internal version of this method: {@link * dagger.producers.Producers#immediateProducer(Object)}. */ @Deprecated public static Producer immediateProducer(T value) { return dagger.producers.Producers.immediateProducer(value); } /** * Returns a producer that fails with the given exception. * * @deprecated Prefer the non-internal version of this method: {@link * dagger.producers.Producers#immediateFailedProducer(Throwable)}. */ @Deprecated public static Producer immediateFailedProducer(Throwable throwable) { return dagger.producers.Producers.immediateFailedProducer(throwable); } /** * Returns a new view of the given {@code producer} if and only if it is a {@link * CancellableProducer}. Cancelling the returned producer's future will not cancel the underlying * task for the given producer. * * @throws IllegalArgumentException if {@code producer} is not a {@code CancellableProducer} */ public static Producer nonCancellationPropagatingViewOf(Producer producer) { // This is a hack until we change the types of Producer fields to be CancellableProducer or // some other type. if (producer instanceof CancellableProducer) { return ((CancellableProducer) producer).newDependencyView(); } throw new IllegalArgumentException( "nonCancellationPropagatingViewOf called with non-CancellableProducer: " + producer); } /** * Returns a new view of the given {@code producer} for use as an entry point in a production * component, if and only if it is a {@link CancellableProducer}. When the returned producer's * future is cancelled, the given {@code cancellable} will also be cancelled. * * @throws IllegalArgumentException if {@code producer} is not a {@code CancellableProducer} */ public static Producer entryPointViewOf( Producer producer, CancellationListener cancellationListener) { // This is a hack until we change the types of Producer fields to be CancellableProducer or // some other type. if (producer instanceof CancellableProducer) { return ((CancellableProducer) producer).newEntryPointView(cancellationListener); } throw new IllegalArgumentException( "entryPointViewOf called with non-CancellableProducer: " + producer); } /** * Calls {@code cancel} on the given {@code producer} if it is a {@link CancellableProducer}. * * @throws IllegalArgumentException if {@code producer} is not a {@code CancellableProducer} */ public static void cancel(Producer producer, boolean mayInterruptIfRunning) { // This is a hack until we change the types of Producer fields to be CancellableProducer or // some other type. if (producer instanceof CancellableProducer) { ((CancellableProducer) producer).cancel(mayInterruptIfRunning); } else { throw new IllegalArgumentException("cancel called with non-CancellableProducer: " + producer); } } private static final Producer> EMPTY_MAP_PRODUCER = dagger.producers.Producers.>immediateProducer(ImmutableMap.of()); @SuppressWarnings("unchecked") // safe contravariant cast public static Producer> emptyMapProducer() { return (Producer>) (Producer) EMPTY_MAP_PRODUCER; } /** * A {@link CancellableProducer} which can't be cancelled because it represents an * already-completed task. */ private abstract static class CompletedProducer implements CancellableProducer { @Override public void cancel(boolean mayInterruptIfRunning) {} @Override public Producer newDependencyView() { return this; } @Override public Producer newEntryPointView(CancellationListener cancellationListener) { return this; } } private Producers() {} }