/* * Copyright (C) 2014 The Dagger Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dagger.internal; import static dagger.internal.DaggerCollections.hasDuplicates; import static dagger.internal.DaggerCollections.newHashSetWithExpectedSize; import static dagger.internal.DaggerCollections.presizedList; import static dagger.internal.Preconditions.checkNotNull; import static java.util.Collections.emptySet; import static java.util.Collections.unmodifiableSet; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Set; import javax.inject.Provider; /** * A {@link Factory} implementation used to implement {@link Set} bindings. This factory always * returns a new {@link Set} instance for each call to {@link #get} (as required by {@link Factory}) * whose elements are populated by subsequent calls to their {@link Provider#get} methods. */ public final class SetFactory<T> implements Factory<Set<T>> { private static final Factory<Set<Object>> EMPTY_FACTORY = InstanceFactory.create(emptySet()); @SuppressWarnings({"unchecked", "rawtypes"}) // safe covariant cast public static <T> Factory<Set<T>> empty() { return (Factory) EMPTY_FACTORY; } /** * Constructs a new {@link Builder} for a {@link SetFactory} with {@code individualProviderSize} * individual {@code Provider<T>} and {@code collectionProviderSize} {@code * Provider<Collection<T>>} instances. */ public static <T> Builder<T> builder(int individualProviderSize, int collectionProviderSize) { return new Builder<T>(individualProviderSize, collectionProviderSize); } /** * A builder to accumulate {@code Provider<T>} and {@code Provider<Collection<T>>} instances. * These are only intended to be single-use and from within generated code. Do <em>NOT</em> add * providers after calling {@link #build()}. */ public static final class Builder<T> { private final List<Provider<T>> individualProviders; private final List<Provider<Collection<T>>> collectionProviders; private Builder(int individualProviderSize, int collectionProviderSize) { individualProviders = presizedList(individualProviderSize); collectionProviders = presizedList(collectionProviderSize); } @SuppressWarnings("unchecked") public Builder<T> addProvider(Provider<? extends T> individualProvider) { assert individualProvider != null : "Codegen error? Null provider"; // TODO(ronshapiro): Store a List<? extends Provider<T>> and avoid the cast to Provider<T> individualProviders.add((Provider<T>) individualProvider); return this; } @SuppressWarnings("unchecked") public Builder<T> addCollectionProvider( Provider<? extends Collection<? extends T>> collectionProvider) { assert collectionProvider != null : "Codegen error? Null provider"; collectionProviders.add((Provider<Collection<T>>) collectionProvider); return this; } public SetFactory<T> build() { assert !hasDuplicates(individualProviders) : "Codegen error? Duplicates in the provider list"; assert !hasDuplicates(collectionProviders) : "Codegen error? Duplicates in the provider list"; return new SetFactory<T>(individualProviders, collectionProviders); } } private final List<Provider<T>> individualProviders; private final List<Provider<Collection<T>>> collectionProviders; private SetFactory( List<Provider<T>> individualProviders, List<Provider<Collection<T>>> collectionProviders) { this.individualProviders = individualProviders; this.collectionProviders = collectionProviders; } /** * Returns a {@link Set} that contains the elements given by each of the providers. * * @throws NullPointerException if any of the delegate {@link Set} instances or elements therein * are {@code null} */ @Override public Set<T> get() { int size = individualProviders.size(); // Profiling revealed that this method was a CPU-consuming hotspot in some applications, so // these loops were changed to use c-style for. Versus enhanced for-each loops, C-style for is // faster for ArrayLists, at least through Java 8. List<Collection<T>> providedCollections = new ArrayList<Collection<T>>(collectionProviders.size()); for (int i = 0, c = collectionProviders.size(); i < c; i++) { Collection<T> providedCollection = collectionProviders.get(i).get(); size += providedCollection.size(); providedCollections.add(providedCollection); } Set<T> providedValues = newHashSetWithExpectedSize(size); for (int i = 0, c = individualProviders.size(); i < c; i++) { providedValues.add(checkNotNull(individualProviders.get(i).get())); } for (int i = 0, c = providedCollections.size(); i < c; i++) { for (T element : providedCollections.get(i)) { providedValues.add(checkNotNull(element)); } } return unmodifiableSet(providedValues); } }