dagger.producers.internal.SetOfProducedProducer Maven / Gradle / Ivy
Show all versions of dagger-producers Show documentation
/*
* Copyright (C) 2015 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.MoreExecutors.directExecutor;
import static dagger.internal.DaggerCollections.hasDuplicates;
import static dagger.internal.DaggerCollections.presizedList;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableSet;
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.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
/**
* A {@link Producer} implementation used to implement {@link Set} bindings. This producer returns a
* future {@code Set>} whose elements are populated by subsequent calls to the delegate
* {@link Producer#get} methods.
*/
public final class SetOfProducedProducer extends AbstractProducer>> {
public static Producer> empty() {
return SetProducer.empty();
}
/**
* Constructs a new {@link Builder} for a {@link SetProducer} with {@code individualProducerSize}
* individual {@code Producer} and {@code collectionProducerSize} {@code
* Producer>} instances.
*/
public static Builder builder(int individualProducerSize, int collectionProducerSize) {
return new Builder(individualProducerSize, collectionProducerSize);
}
/**
* A builder to accumulate {@code Producer} and {@code Producer>} instances.
* These are only intended to be single-use and from within generated code. Do NOT add
* producers after calling {@link #build()}.
*/
public static final class Builder {
private final List> individualProducers;
private final List>> collectionProducers;
private Builder(int individualProducerSize, int collectionProducerSize) {
individualProducers = presizedList(individualProducerSize);
collectionProducers = presizedList(collectionProducerSize);
}
@SuppressWarnings("unchecked")
public Builder addProducer(Producer extends T> individualProducer) {
assert individualProducer != null : "Codegen error? Null producer";
individualProducers.add((Producer) individualProducer);
return this;
}
@SuppressWarnings("unchecked")
public Builder addCollectionProducer(
Producer extends Collection extends T>> multipleProducer) {
assert multipleProducer != null : "Codegen error? Null producer";
collectionProducers.add((Producer>) multipleProducer);
return this;
}
public SetOfProducedProducer build() {
assert !hasDuplicates(individualProducers)
: "Codegen error? Duplicates in the producer list";
assert !hasDuplicates(collectionProducers)
: "Codegen error? Duplicates in the producer list";
return new SetOfProducedProducer(individualProducers, collectionProducers);
}
}
private final List> individualProducers;
private final List>> collectionProducers;
private SetOfProducedProducer(
List> individualProducers, List>> collectionProducers) {
this.individualProducers = individualProducers;
this.collectionProducers = collectionProducers;
}
/**
* Returns a future {@link Set} of {@link Produced} elements given by each of the producers.
*
* If any of the delegate collections, or any elements therein, are null, then that
* corresponding {@code Produced} element will fail with a NullPointerException.
*
*
Canceling this future will attempt to cancel all of the component futures; but if any of the
* delegate futures fail or are canceled, this future succeeds, with the appropriate failed {@link
* Produced}.
*
* @throws NullPointerException if any of the delegate producers return null
*/
@Override
public ListenableFuture>> compute() {
List>>> futureProducedCollections =
new ArrayList>>>(
individualProducers.size() + collectionProducers.size());
for (Producer producer : individualProducers) {
// TODO(ronshapiro): Don't require individual productions to be added to a collection just to
// be materialized into futureProducedCollections.
futureProducedCollections.add(
Producers.createFutureProduced(
Producers.createFutureSingletonSet(checkNotNull(producer.get()))));
}
for (Producer> producer : collectionProducers) {
futureProducedCollections.add(Producers.createFutureProduced(checkNotNull(producer.get())));
}
return Futures.transform(
Futures.allAsList(futureProducedCollections),
new Function>>, Set>>() {
@Override
public Set> apply(
List>> producedCollections) {
ImmutableSet.Builder> builder = ImmutableSet.builder();
for (Produced extends Collection> producedCollection : producedCollections) {
try {
Collection collection = producedCollection.get();
if (collection == null) {
// TODO(beder): This is a vague exception. Can we somehow point to the failing
// producer? See the similar comment in the component writer about null
// provisions.
builder.add(
Produced.failed(
new NullPointerException(
"Cannot contribute a null collection into a producer set binding when"
+ " it's injected as Set>.")));
} else {
for (T value : collection) {
if (value == null) {
builder.add(
Produced.failed(
new NullPointerException(
"Cannot contribute a null element into a producer set binding"
+ " when it's injected as Set>.")));
} else {
builder.add(Produced.successful(value));
}
}
}
} catch (ExecutionException e) {
builder.add(Produced.failed(e.getCause()));
}
}
return builder.build();
}
},
directExecutor());
}
}