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