Home | History | Annotate | Download | only in internal
      1 /*
      2  * Copyright (C) 2015 Google, Inc.
      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 package dagger.producers.internal;
     17 
     18 import com.google.common.base.Function;
     19 import com.google.common.collect.ImmutableSet;
     20 import com.google.common.util.concurrent.Futures;
     21 import com.google.common.util.concurrent.ListenableFuture;
     22 import dagger.producers.Produced;
     23 import dagger.producers.Producer;
     24 import dagger.producers.monitoring.ProducerMonitor;
     25 import java.util.ArrayList;
     26 import java.util.List;
     27 import java.util.Set;
     28 import java.util.concurrent.ExecutionException;
     29 
     30 /**
     31  * A {@link Producer} implementation used to implement {@link Set} bindings. This producer returns a
     32  * future {@code Set<Produced<T>>} whose elements are populated by subsequent calls to the delegate
     33  * {@link Producer#get} methods.
     34  *
     35  * @author Jesse Beder
     36  * @since 2.0
     37  */
     38 public final class SetOfProducedProducer<T> extends AbstractProducer<Set<Produced<T>>> {
     39   /**
     40    * Returns a new producer that creates {@link Set} futures from the union of the given
     41    * {@link Producer} instances.
     42    */
     43   @SafeVarargs
     44   public static <T> Producer<Set<Produced<T>>> create(Producer<Set<T>>... producers) {
     45     return new SetOfProducedProducer<T>(ImmutableSet.copyOf(producers));
     46   }
     47 
     48   private final ImmutableSet<Producer<Set<T>>> contributingProducers;
     49 
     50   private SetOfProducedProducer(ImmutableSet<Producer<Set<T>>> contributingProducers) {
     51     this.contributingProducers = contributingProducers;
     52   }
     53 
     54   /**
     55    * Returns a future {@link Set} of {@link Produced} values whose iteration order is that of the
     56    * elements given by each of the producers, which are invoked in the order given at creation.
     57    *
     58    * <p>If any of the delegate sets, or any elements therein, are null, then that corresponding
     59    * {@code Produced} element will fail with a NullPointerException.
     60    *
     61    * <p>Canceling this future will attempt to cancel all of the component futures; but if any of the
     62    * delegate futures fail or are canceled, this future succeeds, with the appropriate failed
     63    * {@link Produced}.
     64    *
     65    * @throws NullPointerException if any of the delegate producers return null
     66    */
     67   @Override
     68   public ListenableFuture<Set<Produced<T>>> compute(ProducerMonitor unusedMonitor) {
     69     List<ListenableFuture<Produced<Set<T>>>> futureProducedSets =
     70         new ArrayList<ListenableFuture<Produced<Set<T>>>>(contributingProducers.size());
     71     for (Producer<Set<T>> producer : contributingProducers) {
     72       ListenableFuture<Set<T>> futureSet = producer.get();
     73       if (futureSet == null) {
     74         throw new NullPointerException(producer + " returned null");
     75       }
     76       futureProducedSets.add(Producers.createFutureProduced(futureSet));
     77     }
     78     return Futures.transform(
     79         Futures.allAsList(futureProducedSets),
     80         new Function<List<Produced<Set<T>>>, Set<Produced<T>>>() {
     81           @Override
     82           public Set<Produced<T>> apply(List<Produced<Set<T>>> producedSets) {
     83             ImmutableSet.Builder<Produced<T>> builder = ImmutableSet.builder();
     84             for (Produced<Set<T>> producedSet : producedSets) {
     85               try {
     86                 Set<T> set = producedSet.get();
     87                 if (set == null) {
     88                   // TODO(beder): This is a vague exception. Can we somehow point to the failing
     89                   // producer? See the similar comment in the component writer about null
     90                   // provisions.
     91                   builder.add(
     92                       Produced.<T>failed(
     93                           new NullPointerException(
     94                               "Cannot contribute a null set into a producer set binding when it's"
     95                                   + " injected as Set<Produced<T>>.")));
     96                 } else {
     97                   for (T value : set) {
     98                     if (value == null) {
     99                       builder.add(
    100                           Produced.<T>failed(
    101                               new NullPointerException(
    102                                   "Cannot contribute a null element into a producer set binding"
    103                                       + " when it's injected as Set<Produced<T>>.")));
    104                     } else {
    105                       builder.add(Produced.successful(value));
    106                     }
    107                   }
    108                 }
    109               } catch (ExecutionException e) {
    110                 builder.add(Produced.<T>failed(e.getCause()));
    111               }
    112             }
    113             return builder.build();
    114           }
    115         });
    116   }
    117 }
    118