aboutsummaryrefslogtreecommitdiff
path: root/java/dagger/internal/SetFactory.java
blob: 349399b3e1f09f96bda287be121f0e78f3c9108a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/*
 * 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);
  }
}