• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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