xref: /aosp_15_r20/external/dagger2/java/dagger/producers/internal/SetOfProducedProducer.java (revision f585d8a307d0621d6060bd7e80091fdcbf94fe27)
1 /*
2  * Copyright (C) 2015 The Dagger Authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package dagger.producers.internal;
18 
19 import static com.google.common.base.Preconditions.checkNotNull;
20 import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
21 import static dagger.internal.DaggerCollections.hasDuplicates;
22 import static dagger.internal.DaggerCollections.presizedList;
23 
24 import com.google.common.base.Function;
25 import com.google.common.collect.ImmutableSet;
26 import com.google.common.util.concurrent.Futures;
27 import com.google.common.util.concurrent.ListenableFuture;
28 import dagger.producers.Produced;
29 import dagger.producers.Producer;
30 import java.util.ArrayList;
31 import java.util.Collection;
32 import java.util.List;
33 import java.util.Set;
34 import java.util.concurrent.ExecutionException;
35 
36 /**
37  * A {@link Producer} implementation used to implement {@link Set} bindings. This producer returns a
38  * future {@code Set<Produced<T>>} whose elements are populated by subsequent calls to the delegate
39  * {@link Producer#get} methods.
40  */
41 public final class SetOfProducedProducer<T> extends AbstractProducer<Set<Produced<T>>> {
empty()42   public static <T> Producer<Set<T>> empty() {
43     return SetProducer.empty();
44   }
45 
46   /**
47    * Constructs a new {@link Builder} for a {@link SetProducer} with {@code individualProducerSize}
48    * individual {@code Producer<T>} and {@code collectionProducerSize} {@code
49    * Producer<Collection<T>>} instances.
50    */
builder(int individualProducerSize, int collectionProducerSize)51   public static <T> Builder<T> builder(int individualProducerSize, int collectionProducerSize) {
52     return new Builder<T>(individualProducerSize, collectionProducerSize);
53   }
54 
55   /**
56    * A builder to accumulate {@code Producer<T>} and {@code Producer<Collection<T>>} instances.
57    * These are only intended to be single-use and from within generated code. Do <em>NOT</em> add
58    * producers after calling {@link #build()}.
59    */
60   public static final class Builder<T> {
61     private final List<Producer<T>> individualProducers;
62     private final List<Producer<Collection<T>>> collectionProducers;
63 
Builder(int individualProducerSize, int collectionProducerSize)64     private Builder(int individualProducerSize, int collectionProducerSize) {
65       individualProducers = presizedList(individualProducerSize);
66       collectionProducers = presizedList(collectionProducerSize);
67     }
68 
69     @SuppressWarnings("unchecked")
addProducer(Producer<? extends T> individualProducer)70     public Builder<T> addProducer(Producer<? extends T> individualProducer) {
71       assert individualProducer != null : "Codegen error? Null producer";
72       individualProducers.add((Producer<T>) individualProducer);
73       return this;
74     }
75 
76     @SuppressWarnings("unchecked")
addCollectionProducer( Producer<? extends Collection<? extends T>> multipleProducer)77     public Builder<T> addCollectionProducer(
78         Producer<? extends Collection<? extends T>> multipleProducer) {
79       assert multipleProducer != null : "Codegen error? Null producer";
80       collectionProducers.add((Producer<Collection<T>>) multipleProducer);
81       return this;
82     }
83 
build()84     public SetOfProducedProducer<T> build() {
85       assert !hasDuplicates(individualProducers)
86           : "Codegen error?  Duplicates in the producer list";
87       assert !hasDuplicates(collectionProducers)
88           : "Codegen error?  Duplicates in the producer list";
89 
90       return new SetOfProducedProducer<T>(individualProducers, collectionProducers);
91     }
92   }
93 
94   private final List<Producer<T>> individualProducers;
95   private final List<Producer<Collection<T>>> collectionProducers;
96 
SetOfProducedProducer( List<Producer<T>> individualProducers, List<Producer<Collection<T>>> collectionProducers)97   private SetOfProducedProducer(
98       List<Producer<T>> individualProducers, List<Producer<Collection<T>>> collectionProducers) {
99     this.individualProducers = individualProducers;
100     this.collectionProducers = collectionProducers;
101   }
102 
103   /**
104    * Returns a future {@link Set} of {@link Produced} elements given by each of the producers.
105    *
106    * <p>If any of the delegate collections, or any elements therein, are null, then that
107    * corresponding {@code Produced} element will fail with a NullPointerException.
108    *
109    * <p>Canceling this future will attempt to cancel all of the component futures; but if any of the
110    * delegate futures fail or are canceled, this future succeeds, with the appropriate failed {@link
111    * Produced}.
112    *
113    * @throws NullPointerException if any of the delegate producers return null
114    */
115   @Override
compute()116   public ListenableFuture<Set<Produced<T>>> compute() {
117     List<ListenableFuture<? extends Produced<? extends Collection<T>>>> futureProducedCollections =
118         new ArrayList<ListenableFuture<? extends Produced<? extends Collection<T>>>>(
119             individualProducers.size() + collectionProducers.size());
120     for (Producer<T> producer : individualProducers) {
121       // TODO(ronshapiro): Don't require individual productions to be added to a collection just to
122       // be materialized into futureProducedCollections.
123       futureProducedCollections.add(
124           Producers.createFutureProduced(
125               Producers.createFutureSingletonSet(checkNotNull(producer.get()))));
126     }
127     for (Producer<Collection<T>> producer : collectionProducers) {
128       futureProducedCollections.add(Producers.createFutureProduced(checkNotNull(producer.get())));
129     }
130 
131     return Futures.transform(
132         Futures.allAsList(futureProducedCollections),
133         new Function<List<Produced<? extends Collection<T>>>, Set<Produced<T>>>() {
134           @Override
135           public Set<Produced<T>> apply(
136               List<Produced<? extends Collection<T>>> producedCollections) {
137             ImmutableSet.Builder<Produced<T>> builder = ImmutableSet.builder();
138             for (Produced<? extends Collection<T>> producedCollection : producedCollections) {
139               try {
140                 Collection<T> collection = producedCollection.get();
141                 if (collection == null) {
142                   // TODO(beder): This is a vague exception. Can we somehow point to the failing
143                   // producer? See the similar comment in the component writer about null
144                   // provisions.
145                   builder.add(
146                       Produced.<T>failed(
147                           new NullPointerException(
148                               "Cannot contribute a null collection into a producer set binding when"
149                                   + " it's injected as Set<Produced<T>>.")));
150                 } else {
151                   for (T value : collection) {
152                     if (value == null) {
153                       builder.add(
154                           Produced.<T>failed(
155                               new NullPointerException(
156                                   "Cannot contribute a null element into a producer set binding"
157                                       + " when it's injected as Set<Produced<T>>.")));
158                     } else {
159                       builder.add(Produced.successful(value));
160                     }
161                   }
162                 }
163               } catch (ExecutionException e) {
164                 builder.add(Produced.<T>failed(e.getCause()));
165               }
166             }
167             return builder.build();
168           }
169         },
170         directExecutor());
171   }
172 }
173