Home | History | Annotate | Download | only in internal
      1 /*
      2  * Copyright (C) 2014 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.internal;
     17 
     18 import java.util.ArrayList;
     19 import java.util.Arrays;
     20 import java.util.HashSet;
     21 import java.util.List;
     22 import java.util.Set;
     23 import javax.inject.Provider;
     24 
     25 import static dagger.internal.Collections.newLinkedHashSetWithExpectedSize;
     26 import static java.util.Collections.unmodifiableSet;
     27 
     28 /**
     29  * A {@link Factory} implementation used to implement {@link Set} bindings. This factory always
     30  * returns a new {@link Set} instance for each call to {@link #get} (as required by {@link Factory})
     31  * whose elements are populated by subsequent calls to their {@link Provider#get} methods.
     32  *
     33  * @author Gregory Kick
     34  * @since 2.0
     35  */
     36 public final class SetFactory<T> implements Factory<Set<T>> {
     37   /**
     38    * A message for NPEs that trigger on bad argument lists.
     39    */
     40   private static final String ARGUMENTS_MUST_BE_NON_NULL =
     41       "SetFactory.create() requires its arguments to be non-null";
     42 
     43   /**
     44    * Returns the supplied factory.  If there's just one factory, there's no need to wrap it or its
     45    * result.
     46    */
     47   public static <T> Factory<Set<T>> create(Factory<Set<T>> factory) {
     48     assert factory != null : ARGUMENTS_MUST_BE_NON_NULL;
     49     return factory;
     50   }
     51 
     52   /**
     53    * Returns a new factory that creates {@link Set} instances that form the union of the given
     54    * {@link Provider} instances.  Callers must not modify the providers array after invoking this
     55    * method; no copy is made.
     56    */
     57   public static <T> Factory<Set<T>> create(
     58       @SuppressWarnings("unchecked") Provider<Set<T>>... providers) {
     59     assert providers != null : ARGUMENTS_MUST_BE_NON_NULL;
     60 
     61     List<Provider<Set<T>>> contributingProviders = Arrays.asList(providers);
     62 
     63     assert !contributingProviders.contains(null)
     64         : "Codegen error?  Null within provider list.";
     65     assert !hasDuplicates(contributingProviders)
     66         : "Codegen error?  Duplicates in the provider list";
     67 
     68     return new SetFactory<T>(contributingProviders);
     69   }
     70 
     71   /**
     72    * Returns true if at least one pair of items in (@code original) are equals.
     73    */
     74   private static boolean hasDuplicates(List<? extends Object> original) {
     75     Set<Object> asSet = new HashSet<Object>(original);
     76     return original.size() != asSet.size();
     77   }
     78 
     79   private final List<Provider<Set<T>>> contributingProviders;
     80 
     81   private SetFactory(List<Provider<Set<T>>> contributingProviders) {
     82     this.contributingProviders = contributingProviders;
     83   }
     84 
     85   /**
     86    * Returns a {@link Set} whose iteration order is that of the elements given by each of the
     87    * providers, which are invoked in the order given at creation.
     88    *
     89    * @throws NullPointerException if any of the delegate {@link Set} instances or elements therein
     90    *     are {@code null}
     91    */
     92   @Override
     93   public Set<T> get() {
     94     int size = 0;
     95 
     96     // Profiling revealed that this method was a CPU-consuming hotspot in some applications, so
     97     // these loops were changed to use c-style for.  Versus enhanced for-each loops, C-style for is
     98     // faster for ArrayLists, at least through Java 8.
     99 
    100     List<Set<T>> providedSets = new ArrayList<Set<T>>(contributingProviders.size());
    101     for (int i = 0, c = contributingProviders.size(); i < c; i++) {
    102       Provider<Set<T>> provider = contributingProviders.get(i);
    103       Set<T> providedSet = provider.get();
    104       if (providedSet == null) {
    105         throw new NullPointerException(provider + " returned null");
    106       }
    107       providedSets.add(providedSet);
    108       size += providedSet.size();
    109     }
    110 
    111     Set<T> result = newLinkedHashSetWithExpectedSize(size);
    112     for (int i = 0, c = providedSets.size(); i < c; i++) {
    113       for (T element : providedSets.get(i)) {
    114         if (element == null) {
    115           throw new NullPointerException("a null element was provided");
    116         }
    117         result.add(element);
    118       }
    119     }
    120     return unmodifiableSet(result);
    121   }
    122 }
    123