/* * 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 implements Factory> { private static final Factory> EMPTY_FACTORY = InstanceFactory.create(emptySet()); @SuppressWarnings({"unchecked", "rawtypes"}) // safe covariant cast public static Factory> empty() { return (Factory) EMPTY_FACTORY; } /** * Constructs a new {@link Builder} for a {@link SetFactory} with {@code individualProviderSize} * individual {@code Provider} and {@code collectionProviderSize} {@code * Provider>} instances. */ public static Builder builder(int individualProviderSize, int collectionProviderSize) { return new Builder(individualProviderSize, collectionProviderSize); } /** * A builder to accumulate {@code Provider} and {@code Provider>} instances. * These are only intended to be single-use and from within generated code. Do NOT add * providers after calling {@link #build()}. */ public static final class Builder { private final List> individualProviders; private final List>> collectionProviders; private Builder(int individualProviderSize, int collectionProviderSize) { individualProviders = presizedList(individualProviderSize); collectionProviders = presizedList(collectionProviderSize); } @SuppressWarnings("unchecked") public Builder addProvider(Provider individualProvider) { assert individualProvider != null : "Codegen error? Null provider"; // TODO(ronshapiro): Store a List> and avoid the cast to Provider individualProviders.add((Provider) individualProvider); return this; } @SuppressWarnings("unchecked") public Builder addCollectionProvider( Provider> collectionProvider) { assert collectionProvider != null : "Codegen error? Null provider"; collectionProviders.add((Provider>) collectionProvider); return this; } public SetFactory build() { assert !hasDuplicates(individualProviders) : "Codegen error? Duplicates in the provider list"; assert !hasDuplicates(collectionProviders) : "Codegen error? Duplicates in the provider list"; return new SetFactory(individualProviders, collectionProviders); } } private final List> individualProviders; private final List>> collectionProviders; private SetFactory( List> individualProviders, List>> 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 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> providedCollections = new ArrayList>(collectionProviders.size()); for (int i = 0, c = collectionProviders.size(); i < c; i++) { Collection providedCollection = collectionProviders.get(i).get(); size += providedCollection.size(); providedCollections.add(providedCollection); } Set 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); } }