1/**
2 * Copyright (C) 2008 Google Inc.
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
17package com.google.inject.multibindings;
18
19import static com.google.common.base.Predicates.equalTo;
20import static com.google.common.primitives.Ints.MAX_POWER_OF_TWO;
21import static com.google.common.collect.Iterables.filter;
22import static com.google.common.collect.Iterables.getOnlyElement;
23import static com.google.inject.multibindings.Element.Type.MULTIBINDER;
24import static com.google.inject.name.Names.named;
25
26import com.google.common.base.Objects;
27import com.google.common.collect.ImmutableList;
28import com.google.common.collect.ImmutableSet;
29import com.google.common.collect.Lists;
30import com.google.common.collect.Sets;
31import com.google.inject.AbstractModule;
32import com.google.inject.Binder;
33import com.google.inject.Binding;
34import com.google.inject.ConfigurationException;
35import com.google.inject.Inject;
36import com.google.inject.Injector;
37import com.google.inject.Key;
38import com.google.inject.Module;
39import com.google.inject.Provider;
40import com.google.inject.TypeLiteral;
41import com.google.inject.binder.LinkedBindingBuilder;
42import com.google.inject.internal.Errors;
43import com.google.inject.spi.BindingTargetVisitor;
44import com.google.inject.spi.Dependency;
45import com.google.inject.spi.HasDependencies;
46import com.google.inject.spi.Message;
47import com.google.inject.spi.ProviderInstanceBinding;
48import com.google.inject.spi.ProviderWithDependencies;
49import com.google.inject.spi.ProviderWithExtensionVisitor;
50import com.google.inject.spi.Toolable;
51import com.google.inject.util.Types;
52
53import java.lang.annotation.Annotation;
54import java.lang.reflect.Type;
55import java.util.Collection;
56import java.util.LinkedHashMap;
57import java.util.List;
58import java.util.Map;
59import java.util.Set;
60
61/**
62 * An API to bind multiple values separately, only to later inject them as a
63 * complete collection. Multibinder is intended for use in your application's
64 * module:
65 * <pre><code>
66 * public class SnacksModule extends AbstractModule {
67 *   protected void configure() {
68 *     Multibinder&lt;Snack&gt; multibinder
69 *         = Multibinder.newSetBinder(binder(), Snack.class);
70 *     multibinder.addBinding().toInstance(new Twix());
71 *     multibinder.addBinding().toProvider(SnickersProvider.class);
72 *     multibinder.addBinding().to(Skittles.class);
73 *   }
74 * }</code></pre>
75 *
76 * <p>With this binding, a {@link Set}{@code <Snack>} can now be injected:
77 * <pre><code>
78 * class SnackMachine {
79 *   {@literal @}Inject
80 *   public SnackMachine(Set&lt;Snack&gt; snacks) { ... }
81 * }</code></pre>
82 *
83 * If desired, {@link Collection}{@code <Provider<Snack>>} can also be injected.
84 *
85 * <p>Contributing multibindings from different modules is supported. For
86 * example, it is okay for both {@code CandyModule} and {@code ChipsModule}
87 * to create their own {@code Multibinder<Snack>}, and to each contribute
88 * bindings to the set of snacks. When that set is injected, it will contain
89 * elements from both modules.
90 *
91 * <p>The set's iteration order is consistent with the binding order. This is
92 * convenient when multiple elements are contributed by the same module because
93 * that module can order its bindings appropriately. Avoid relying on the
94 * iteration order of elements contributed by different modules, since there is
95 * no equivalent mechanism to order modules.
96 *
97 * <p>The set is unmodifiable.  Elements can only be added to the set by
98 * configuring the multibinder.  Elements can never be removed from the set.
99 *
100 * <p>Elements are resolved at set injection time. If an element is bound to a
101 * provider, that provider's get method will be called each time the set is
102 * injected (unless the binding is also scoped).
103 *
104 * <p>Annotations are be used to create different sets of the same element
105 * type. Each distinct annotation gets its own independent collection of
106 * elements.
107 *
108 * <p><strong>Elements must be distinct.</strong> If multiple bound elements
109 * have the same value, set injection will fail.
110 *
111 * <p><strong>Elements must be non-null.</strong> If any set element is null,
112 * set injection will fail.
113 *
114 * @author jessewilson@google.com (Jesse Wilson)
115 */
116public abstract class Multibinder<T> {
117  private Multibinder() {}
118
119  /**
120   * Returns a new multibinder that collects instances of {@code type} in a {@link Set} that is
121   * itself bound with no binding annotation.
122   */
123  public static <T> Multibinder<T> newSetBinder(Binder binder, TypeLiteral<T> type) {
124    return newRealSetBinder(binder, Key.get(type));
125  }
126
127  /**
128   * Returns a new multibinder that collects instances of {@code type} in a {@link Set} that is
129   * itself bound with no binding annotation.
130   */
131  public static <T> Multibinder<T> newSetBinder(Binder binder, Class<T> type) {
132    return newRealSetBinder(binder, Key.get(type));
133  }
134
135  /**
136   * Returns a new multibinder that collects instances of {@code type} in a {@link Set} that is
137   * itself bound with {@code annotation}.
138   */
139  public static <T> Multibinder<T> newSetBinder(
140      Binder binder, TypeLiteral<T> type, Annotation annotation) {
141    return newRealSetBinder(binder, Key.get(type, annotation));
142  }
143
144  /**
145   * Returns a new multibinder that collects instances of {@code type} in a {@link Set} that is
146   * itself bound with {@code annotation}.
147   */
148  public static <T> Multibinder<T> newSetBinder(
149      Binder binder, Class<T> type, Annotation annotation) {
150    return newRealSetBinder(binder, Key.get(type, annotation));
151  }
152
153  /**
154   * Returns a new multibinder that collects instances of {@code type} in a {@link Set} that is
155   * itself bound with {@code annotationType}.
156   */
157  public static <T> Multibinder<T> newSetBinder(Binder binder, TypeLiteral<T> type,
158      Class<? extends Annotation> annotationType) {
159    return newRealSetBinder(binder, Key.get(type, annotationType));
160  }
161
162  /**
163   * Returns a new multibinder that collects instances of the key's type in a {@link Set} that is
164   * itself bound with the annotation (if any) of the key.
165   *
166   * @since 4.0
167   */
168  public static <T> Multibinder<T> newSetBinder(Binder binder, Key<T> key) {
169    return newRealSetBinder(binder, key);
170  }
171
172  /**
173   * Implementation of newSetBinder.
174   */
175  static <T> RealMultibinder<T> newRealSetBinder(Binder binder, Key<T> key) {
176    binder = binder.skipSources(RealMultibinder.class, Multibinder.class);
177    RealMultibinder<T> result = new RealMultibinder<T>(binder, key.getTypeLiteral(),
178        key.ofType(setOf(key.getTypeLiteral())));
179    binder.install(result);
180    return result;
181  }
182
183  /**
184   * Returns a new multibinder that collects instances of {@code type} in a {@link Set} that is
185   * itself bound with {@code annotationType}.
186   */
187  public static <T> Multibinder<T> newSetBinder(Binder binder, Class<T> type,
188      Class<? extends Annotation> annotationType) {
189    return newSetBinder(binder, Key.get(type, annotationType));
190  }
191
192  @SuppressWarnings("unchecked") // wrapping a T in a Set safely returns a Set<T>
193  static <T> TypeLiteral<Set<T>> setOf(TypeLiteral<T> elementType) {
194    Type type = Types.setOf(elementType.getType());
195    return (TypeLiteral<Set<T>>) TypeLiteral.get(type);
196  }
197
198  @SuppressWarnings("unchecked")
199  static <T> TypeLiteral<Collection<Provider<T>>> collectionOfProvidersOf(
200      TypeLiteral<T> elementType) {
201    Type providerType = Types.providerOf(elementType.getType());
202    Type type = Types.newParameterizedType(Collection.class, providerType);
203    return (TypeLiteral<Collection<Provider<T>>>) TypeLiteral.get(type);
204  }
205
206  @SuppressWarnings("unchecked")
207  static <T> TypeLiteral<Collection<javax.inject.Provider<T>>> collectionOfJavaxProvidersOf(
208      TypeLiteral<T> elementType) {
209    Type providerType =
210        Types.newParameterizedType(javax.inject.Provider.class, elementType.getType());
211    Type type = Types.newParameterizedType(Collection.class, providerType);
212    return (TypeLiteral<Collection<javax.inject.Provider<T>>>) TypeLiteral.get(type);
213  }
214
215  /**
216   * Configures the bound set to silently discard duplicate elements. When multiple equal values are
217   * bound, the one that gets included is arbitrary. When multiple modules contribute elements to
218   * the set, this configuration option impacts all of them.
219   *
220   * @return this multibinder
221   * @since 3.0
222   */
223  public abstract Multibinder<T> permitDuplicates();
224
225  /**
226   * Returns a binding builder used to add a new element in the set. Each
227   * bound element must have a distinct value. Bound providers will be
228   * evaluated each time the set is injected.
229   *
230   * <p>It is an error to call this method without also calling one of the
231   * {@code to} methods on the returned binding builder.
232   *
233   * <p>Scoping elements independently is supported. Use the {@code in} method
234   * to specify a binding scope.
235   */
236  public abstract LinkedBindingBuilder<T> addBinding();
237
238  /**
239   * The actual multibinder plays several roles:
240   *
241   * <p>As a Multibinder, it acts as a factory for LinkedBindingBuilders for
242   * each of the set's elements. Each binding is given an annotation that
243   * identifies it as a part of this set.
244   *
245   * <p>As a Module, it installs the binding to the set itself. As a module,
246   * this implements equals() and hashcode() in order to trick Guice into
247   * executing its configure() method only once. That makes it so that
248   * multiple multibinders can be created for the same target collection, but
249   * only one is bound. Since the list of bindings is retrieved from the
250   * injector itself (and not the multibinder), each multibinder has access to
251   * all contributions from all multibinders.
252   *
253   * <p>As a Provider, this constructs the set instances.
254   *
255   * <p>We use a subclass to hide 'implements Module, Provider' from the public
256   * API.
257   */
258  static final class RealMultibinder<T> extends Multibinder<T>
259      implements Module, ProviderWithExtensionVisitor<Set<T>>, HasDependencies,
260          MultibinderBinding<Set<T>> {
261
262    private final TypeLiteral<T> elementType;
263    private final String setName;
264    private final Key<Set<T>> setKey;
265    private final Key<Collection<Provider<T>>> collectionOfProvidersKey;
266    private final Key<Collection<javax.inject.Provider<T>>> collectionOfJavaxProvidersKey;
267    private final Key<Boolean> permitDuplicatesKey;
268
269    /* the target injector's binder. non-null until initialization, null afterwards */
270    private Binder binder;
271
272    /* a binding for each element in the set. null until initialization, non-null afterwards */
273    private ImmutableList<Binding<T>> bindings;
274    private Set<Dependency<?>> dependencies;
275
276    /** whether duplicates are allowed. Possibly configured by a different instance */
277    private boolean permitDuplicates;
278
279    private RealMultibinder(Binder binder, TypeLiteral<T> elementType, Key<Set<T>> setKey) {
280      this.binder = checkNotNull(binder, "binder");
281      this.elementType = checkNotNull(elementType, "elementType");
282      this.setKey = checkNotNull(setKey, "setKey");
283      this.collectionOfProvidersKey = setKey.ofType(collectionOfProvidersOf(elementType));
284      this.collectionOfJavaxProvidersKey = setKey.ofType(collectionOfJavaxProvidersOf(elementType));
285      this.setName = RealElement.nameOf(setKey);
286      this.permitDuplicatesKey = Key.get(Boolean.class, named(toString() + " permits duplicates"));
287    }
288
289    public void configure(Binder binder) {
290      checkConfiguration(!isInitialized(), "Multibinder was already initialized");
291
292      binder.bind(setKey).toProvider(this);
293      binder.bind(collectionOfProvidersKey).toProvider(
294          new RealMultibinderCollectionOfProvidersProvider());
295
296      // The collection this exposes is internally an ImmutableList, so it's OK to massage
297      // the guice Provider to javax Provider in the value (since the guice Provider implements
298      // javax Provider).
299      @SuppressWarnings("unchecked")
300      Key key = (Key) collectionOfProvidersKey;
301      binder.bind(collectionOfJavaxProvidersKey).to(key);
302    }
303
304    @Override public Multibinder<T> permitDuplicates() {
305      binder.install(new PermitDuplicatesModule(permitDuplicatesKey));
306      return this;
307    }
308
309    Key<T> getKeyForNewItem() {
310      checkConfiguration(!isInitialized(), "Multibinder was already initialized");
311      return Key.get(elementType, new RealElement(setName, MULTIBINDER, ""));
312    }
313
314    @Override public LinkedBindingBuilder<T> addBinding() {
315      return binder.bind(getKeyForNewItem());
316    }
317
318    /**
319     * Invoked by Guice at Injector-creation time to prepare providers for each
320     * element in this set. At this time the set's size is known, but its
321     * contents are only evaluated when get() is invoked.
322     */
323    @Toolable @Inject void initialize(Injector injector) {
324      List<Binding<T>> bindings = Lists.newArrayList();
325      Set<Indexer.IndexedBinding> index = Sets.newHashSet();
326      Indexer indexer = new Indexer(injector);
327      List<Dependency<?>> dependencies = Lists.newArrayList();
328      for (Binding<?> entry : injector.findBindingsByType(elementType)) {
329        if (keyMatches(entry.getKey())) {
330          @SuppressWarnings("unchecked") // protected by findBindingsByType()
331          Binding<T> binding = (Binding<T>) entry;
332          if (index.add(binding.acceptTargetVisitor(indexer))) {
333            bindings.add(binding);
334            dependencies.add(Dependency.get(binding.getKey()));
335          }
336        }
337      }
338
339      this.bindings = ImmutableList.copyOf(bindings);
340      this.dependencies = ImmutableSet.copyOf(dependencies);
341      this.permitDuplicates = permitsDuplicates(injector);
342      this.binder = null;
343    }
344
345    // This is forked from com.google.common.collect.Maps.capacity
346    private static int mapCapacity(int numBindings) {
347      if (numBindings < 3) {
348        return numBindings + 1;
349      } else  if (numBindings < MAX_POWER_OF_TWO) {
350        return (int) (numBindings / 0.75F + 1.0F);
351      }
352      return Integer.MAX_VALUE;
353    }
354
355    boolean permitsDuplicates(Injector injector) {
356      return injector.getBindings().containsKey(permitDuplicatesKey);
357    }
358
359    private boolean keyMatches(Key<?> key) {
360      return key.getTypeLiteral().equals(elementType)
361          && key.getAnnotation() instanceof Element
362          && ((Element) key.getAnnotation()).setName().equals(setName)
363          && ((Element) key.getAnnotation()).type() == MULTIBINDER;
364    }
365
366    private boolean isInitialized() {
367      return binder == null;
368    }
369
370    public Set<T> get() {
371      checkConfiguration(isInitialized(), "Multibinder is not initialized");
372
373      Map<T, Binding<T>> result = new LinkedHashMap<T, Binding<T>>(mapCapacity(bindings.size()));
374      for (Binding<T> binding : bindings) {
375        final T newValue = binding.getProvider().get();
376        checkConfiguration(newValue != null,
377            "Set injection failed due to null element bound at: %s",
378            binding.getSource());
379        Binding<T> duplicateBinding = result.put(newValue, binding);
380        if (!permitDuplicates && duplicateBinding != null) {
381          throw newDuplicateValuesException(result, binding, newValue, duplicateBinding);
382        }
383      }
384      return ImmutableSet.copyOf(result.keySet());
385    }
386
387    @SuppressWarnings("unchecked")
388    public <B, V> V acceptExtensionVisitor(
389        BindingTargetVisitor<B, V> visitor,
390        ProviderInstanceBinding<? extends B> binding) {
391      if (visitor instanceof MultibindingsTargetVisitor) {
392        return ((MultibindingsTargetVisitor<Set<T>, V>) visitor).visit(this);
393      } else {
394        return visitor.visit(binding);
395      }
396    }
397
398    String getSetName() {
399      return setName;
400    }
401
402    public TypeLiteral<?> getElementTypeLiteral() {
403      return elementType;
404    }
405
406    public Key<Set<T>> getSetKey() {
407      return setKey;
408    }
409
410    @SuppressWarnings("unchecked")
411    public List<Binding<?>> getElements() {
412      if (isInitialized()) {
413        return (List<Binding<?>>) (List<?>) bindings; // safe because bindings is immutable.
414      } else {
415        throw new UnsupportedOperationException("getElements() not supported for module bindings");
416      }
417    }
418
419    public boolean permitsDuplicates() {
420      if (isInitialized()) {
421        return permitDuplicates;
422      } else {
423        throw new UnsupportedOperationException(
424            "permitsDuplicates() not supported for module bindings");
425      }
426    }
427
428    public boolean containsElement(com.google.inject.spi.Element element) {
429      if (element instanceof Binding) {
430        Binding<?> binding = (Binding<?>) element;
431        return keyMatches(binding.getKey())
432            || binding.getKey().equals(permitDuplicatesKey)
433            || binding.getKey().equals(setKey)
434            || binding.getKey().equals(collectionOfProvidersKey)
435            || binding.getKey().equals(collectionOfJavaxProvidersKey);
436      } else {
437        return false;
438      }
439    }
440
441    public Set<Dependency<?>> getDependencies() {
442      if (!isInitialized()) {
443        return ImmutableSet.<Dependency<?>>of(Dependency.get(Key.get(Injector.class)));
444      } else {
445        return dependencies;
446      }
447    }
448
449    @Override public boolean equals(Object o) {
450      return o instanceof RealMultibinder
451          && ((RealMultibinder<?>) o).setKey.equals(setKey);
452    }
453
454    @Override public int hashCode() {
455      return setKey.hashCode();
456    }
457
458    @Override public String toString() {
459      return (setName.isEmpty() ? "" : setName + " ") + "Multibinder<" + elementType + ">";
460    }
461
462    final class RealMultibinderCollectionOfProvidersProvider
463        implements ProviderWithDependencies<Collection<Provider<T>>> {
464      @Override public Collection<Provider<T>> get() {
465        checkConfiguration(isInitialized(), "Multibinder is not initialized");
466        int size = bindings.size();
467        @SuppressWarnings("unchecked")  // safe because we only put Provider<T> into it.
468        Provider<T>[] providers = new Provider[size];
469        for (int i = 0; i < size; i++) {
470          providers[i] = bindings.get(i).getProvider();
471        }
472        return ImmutableList.copyOf(providers);
473      }
474
475      @Override public Set<Dependency<?>> getDependencies() {
476        if (!isInitialized()) {
477          return ImmutableSet.<Dependency<?>>of(Dependency.get(Key.get(Injector.class)));
478        }
479        ImmutableSet.Builder<Dependency<?>> setBuilder = ImmutableSet.builder();
480        for (Dependency<?> dependency : dependencies) {
481          Key key = dependency.getKey();
482          setBuilder.add(
483              Dependency.get(key.ofType(Types.providerOf(key.getTypeLiteral().getType()))));
484        }
485        return setBuilder.build();
486      }
487
488      Key getCollectionKey() {
489        return RealMultibinder.this.collectionOfProvidersKey;
490      }
491
492      @Override public boolean equals(Object o) {
493        return o instanceof Multibinder.RealMultibinder.RealMultibinderCollectionOfProvidersProvider
494            && ((Multibinder.RealMultibinder.RealMultibinderCollectionOfProvidersProvider) o)
495                .getCollectionKey().equals(getCollectionKey());
496      }
497
498      @Override public int hashCode() {
499        return getCollectionKey().hashCode();
500      }
501    }
502  }
503
504  /**
505   * We install the permit duplicates configuration as its own binding, all by itself. This way,
506   * if only one of a multibinder's users remember to call permitDuplicates(), they're still
507   * permitted.
508   */
509  private static class PermitDuplicatesModule extends AbstractModule {
510    private final Key<Boolean> key;
511
512    PermitDuplicatesModule(Key<Boolean> key) {
513      this.key = key;
514    }
515
516    @Override protected void configure() {
517      bind(key).toInstance(true);
518    }
519
520    @Override public boolean equals(Object o) {
521      return o instanceof PermitDuplicatesModule
522          && ((PermitDuplicatesModule) o).key.equals(key);
523    }
524
525    @Override public int hashCode() {
526      return getClass().hashCode() ^ key.hashCode();
527    }
528  }
529
530  static void checkConfiguration(boolean condition, String format, Object... args) {
531    if (condition) {
532      return;
533    }
534
535    throw new ConfigurationException(ImmutableSet.of(new Message(Errors.format(format, args))));
536  }
537
538  private static <T> ConfigurationException newDuplicateValuesException(
539      Map<T, Binding<T>> existingBindings,
540      Binding<T> binding,
541      final T newValue,
542      Binding<T> duplicateBinding) {
543    T oldValue = getOnlyElement(filter(existingBindings.keySet(), equalTo(newValue)));
544    String oldString = oldValue.toString();
545    String newString = newValue.toString();
546    if (Objects.equal(oldString, newString)) {
547      // When the value strings match, just show the source of the bindings
548      return new ConfigurationException(ImmutableSet.of(new Message(Errors.format(
549          "Set injection failed due to duplicated element \"%s\""
550              + "\n    Bound at %s\n    Bound at %s",
551          newValue,
552          duplicateBinding.getSource(),
553          binding.getSource()))));
554    } else {
555      // When the value strings don't match, include them both as they may be useful for debugging
556      return new ConfigurationException(ImmutableSet.of(new Message(Errors.format(
557          "Set injection failed due to multiple elements comparing equal:"
558              + "\n    \"%s\"\n        bound at %s"
559              + "\n    \"%s\"\n        bound at %s",
560          oldValue,
561          duplicateBinding.getSource(),
562          newValue,
563          binding.getSource()))));
564    }
565  }
566
567  static <T> T checkNotNull(T reference, String name) {
568    if (reference != null) {
569      return reference;
570    }
571
572    NullPointerException npe = new NullPointerException(name);
573    throw new ConfigurationException(ImmutableSet.of(
574        new Message(npe.toString(), npe)));
575  }
576}
577