1 /* 2 * Copyright (C) 2014 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.internal; 18 19 import static dagger.internal.DaggerCollections.hasDuplicates; 20 import static dagger.internal.DaggerCollections.newHashSetWithExpectedSize; 21 import static dagger.internal.DaggerCollections.presizedList; 22 import static dagger.internal.Preconditions.checkNotNull; 23 import static java.util.Collections.emptySet; 24 import static java.util.Collections.unmodifiableSet; 25 26 import java.util.ArrayList; 27 import java.util.Collection; 28 import java.util.List; 29 import java.util.Set; 30 import javax.inject.Provider; 31 32 /** 33 * A {@link Factory} implementation used to implement {@link Set} bindings. This factory always 34 * returns a new {@link Set} instance for each call to {@link #get} (as required by {@link Factory}) 35 * whose elements are populated by subsequent calls to their {@link Provider#get} methods. 36 */ 37 public final class SetFactory<T> implements Factory<Set<T>> { 38 private static final Factory<Set<Object>> EMPTY_FACTORY = InstanceFactory.create(emptySet()); 39 40 @SuppressWarnings({"unchecked", "rawtypes"}) // safe covariant cast empty()41 public static <T> Factory<Set<T>> empty() { 42 return (Factory) EMPTY_FACTORY; 43 } 44 45 /** 46 * Constructs a new {@link Builder} for a {@link SetFactory} with {@code individualProviderSize} 47 * individual {@code Provider<T>} and {@code collectionProviderSize} {@code 48 * Provider<Collection<T>>} instances. 49 */ builder(int individualProviderSize, int collectionProviderSize)50 public static <T> Builder<T> builder(int individualProviderSize, int collectionProviderSize) { 51 return new Builder<T>(individualProviderSize, collectionProviderSize); 52 } 53 54 /** 55 * A builder to accumulate {@code Provider<T>} and {@code Provider<Collection<T>>} instances. 56 * These are only intended to be single-use and from within generated code. Do <em>NOT</em> add 57 * providers after calling {@link #build()}. 58 */ 59 public static final class Builder<T> { 60 private final List<Provider<T>> individualProviders; 61 private final List<Provider<Collection<T>>> collectionProviders; 62 Builder(int individualProviderSize, int collectionProviderSize)63 private Builder(int individualProviderSize, int collectionProviderSize) { 64 individualProviders = presizedList(individualProviderSize); 65 collectionProviders = presizedList(collectionProviderSize); 66 } 67 68 @SuppressWarnings("unchecked") addProvider(Provider<? extends T> individualProvider)69 public Builder<T> addProvider(Provider<? extends T> individualProvider) { 70 assert individualProvider != null : "Codegen error? Null provider"; 71 // TODO(ronshapiro): Store a List<? extends Provider<T>> and avoid the cast to Provider<T> 72 individualProviders.add((Provider<T>) individualProvider); 73 return this; 74 } 75 76 @SuppressWarnings("unchecked") addCollectionProvider( Provider<? extends Collection<? extends T>> collectionProvider)77 public Builder<T> addCollectionProvider( 78 Provider<? extends Collection<? extends T>> collectionProvider) { 79 assert collectionProvider != null : "Codegen error? Null provider"; 80 collectionProviders.add((Provider<Collection<T>>) collectionProvider); 81 return this; 82 } 83 build()84 public SetFactory<T> build() { 85 assert !hasDuplicates(individualProviders) 86 : "Codegen error? Duplicates in the provider list"; 87 assert !hasDuplicates(collectionProviders) 88 : "Codegen error? Duplicates in the provider list"; 89 90 return new SetFactory<T>(individualProviders, collectionProviders); 91 } 92 } 93 94 private final List<Provider<T>> individualProviders; 95 private final List<Provider<Collection<T>>> collectionProviders; 96 SetFactory( List<Provider<T>> individualProviders, List<Provider<Collection<T>>> collectionProviders)97 private SetFactory( 98 List<Provider<T>> individualProviders, List<Provider<Collection<T>>> collectionProviders) { 99 this.individualProviders = individualProviders; 100 this.collectionProviders = collectionProviders; 101 } 102 103 /** 104 * Returns a {@link Set} that contains the elements given by each of the providers. 105 * 106 * @throws NullPointerException if any of the delegate {@link Set} instances or elements therein 107 * are {@code null} 108 */ 109 @Override get()110 public Set<T> get() { 111 int size = individualProviders.size(); 112 // Profiling revealed that this method was a CPU-consuming hotspot in some applications, so 113 // these loops were changed to use c-style for. Versus enhanced for-each loops, C-style for is 114 // faster for ArrayLists, at least through Java 8. 115 116 List<Collection<T>> providedCollections = 117 new ArrayList<Collection<T>>(collectionProviders.size()); 118 for (int i = 0, c = collectionProviders.size(); i < c; i++) { 119 Collection<T> providedCollection = collectionProviders.get(i).get(); 120 size += providedCollection.size(); 121 providedCollections.add(providedCollection); 122 } 123 124 Set<T> providedValues = newHashSetWithExpectedSize(size); 125 for (int i = 0, c = individualProviders.size(); i < c; i++) { 126 providedValues.add(checkNotNull(individualProviders.get(i).get())); 127 } 128 for (int i = 0, c = providedCollections.size(); i < c; i++) { 129 for (T element : providedCollections.get(i)) { 130 providedValues.add(checkNotNull(element)); 131 } 132 } 133 134 return unmodifiableSet(providedValues); 135 } 136 } 137