• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.google.inject.multibindings;
18 
19 import static com.google.common.base.Predicates.equalTo;
20 import static com.google.common.primitives.Ints.MAX_POWER_OF_TWO;
21 import static com.google.common.collect.Iterables.filter;
22 import static com.google.common.collect.Iterables.getOnlyElement;
23 import static com.google.inject.multibindings.Element.Type.MULTIBINDER;
24 import static com.google.inject.name.Names.named;
25 
26 import com.google.common.base.Objects;
27 import com.google.common.collect.ImmutableList;
28 import com.google.common.collect.ImmutableSet;
29 import com.google.common.collect.Lists;
30 import com.google.common.collect.Sets;
31 import com.google.inject.AbstractModule;
32 import com.google.inject.Binder;
33 import com.google.inject.Binding;
34 import com.google.inject.ConfigurationException;
35 import com.google.inject.Inject;
36 import com.google.inject.Injector;
37 import com.google.inject.Key;
38 import com.google.inject.Module;
39 import com.google.inject.Provider;
40 import com.google.inject.TypeLiteral;
41 import com.google.inject.binder.LinkedBindingBuilder;
42 import com.google.inject.internal.Errors;
43 import com.google.inject.spi.BindingTargetVisitor;
44 import com.google.inject.spi.Dependency;
45 import com.google.inject.spi.HasDependencies;
46 import com.google.inject.spi.Message;
47 import com.google.inject.spi.ProviderInstanceBinding;
48 import com.google.inject.spi.ProviderWithDependencies;
49 import com.google.inject.spi.ProviderWithExtensionVisitor;
50 import com.google.inject.spi.Toolable;
51 import com.google.inject.util.Types;
52 
53 import java.lang.annotation.Annotation;
54 import java.lang.reflect.Type;
55 import java.util.Collection;
56 import java.util.LinkedHashMap;
57 import java.util.List;
58 import java.util.Map;
59 import 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  */
116 public abstract class Multibinder<T> {
Multibinder()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    */
newSetBinder(Binder binder, TypeLiteral<T> type)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    */
newSetBinder(Binder binder, Class<T> type)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    */
newSetBinder( Binder binder, TypeLiteral<T> type, Annotation annotation)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    */
newSetBinder( Binder binder, Class<T> type, Annotation annotation)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    */
newSetBinder(Binder binder, TypeLiteral<T> type, Class<? extends Annotation> annotationType)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    */
newSetBinder(Binder binder, Key<T> key)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    */
newRealSetBinder(Binder binder, Key<T> key)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    */
newSetBinder(Binder binder, Class<T> type, Class<? extends Annotation> annotationType)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>
setOf(TypeLiteral<T> elementType)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")
collectionOfProvidersOf( TypeLiteral<T> elementType)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")
collectionOfJavaxProvidersOf( TypeLiteral<T> elementType)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    */
permitDuplicates()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    */
addBinding()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 
RealMultibinder(Binder binder, TypeLiteral<T> elementType, Key<Set<T>> setKey)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 
configure(Binder binder)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 
permitDuplicates()304     @Override public Multibinder<T> permitDuplicates() {
305       binder.install(new PermitDuplicatesModule(permitDuplicatesKey));
306       return this;
307     }
308 
getKeyForNewItem()309     Key<T> getKeyForNewItem() {
310       checkConfiguration(!isInitialized(), "Multibinder was already initialized");
311       return Key.get(elementType, new RealElement(setName, MULTIBINDER, ""));
312     }
313 
addBinding()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      */
initialize(Injector injector)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
mapCapacity(int numBindings)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 
permitsDuplicates(Injector injector)355     boolean permitsDuplicates(Injector injector) {
356       return injector.getBindings().containsKey(permitDuplicatesKey);
357     }
358 
keyMatches(Key<?> key)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 
isInitialized()366     private boolean isInitialized() {
367       return binder == null;
368     }
369 
get()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")
acceptExtensionVisitor( BindingTargetVisitor<B, V> visitor, ProviderInstanceBinding<? extends B> binding)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 
getSetName()398     String getSetName() {
399       return setName;
400     }
401 
getElementTypeLiteral()402     public TypeLiteral<?> getElementTypeLiteral() {
403       return elementType;
404     }
405 
getSetKey()406     public Key<Set<T>> getSetKey() {
407       return setKey;
408     }
409 
410     @SuppressWarnings("unchecked")
getElements()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 
permitsDuplicates()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 
containsElement(com.google.inject.spi.Element element)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 
getDependencies()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 
equals(Object o)449     @Override public boolean equals(Object o) {
450       return o instanceof RealMultibinder
451           && ((RealMultibinder<?>) o).setKey.equals(setKey);
452     }
453 
hashCode()454     @Override public int hashCode() {
455       return setKey.hashCode();
456     }
457 
toString()458     @Override public String toString() {
459       return (setName.isEmpty() ? "" : setName + " ") + "Multibinder<" + elementType + ">";
460     }
461 
462     final class RealMultibinderCollectionOfProvidersProvider
463         implements ProviderWithDependencies<Collection<Provider<T>>> {
get()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 
getDependencies()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 
getCollectionKey()488       Key getCollectionKey() {
489         return RealMultibinder.this.collectionOfProvidersKey;
490       }
491 
equals(Object o)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 
hashCode()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 
PermitDuplicatesModule(Key<Boolean> key)512     PermitDuplicatesModule(Key<Boolean> key) {
513       this.key = key;
514     }
515 
configure()516     @Override protected void configure() {
517       bind(key).toInstance(true);
518     }
519 
equals(Object o)520     @Override public boolean equals(Object o) {
521       return o instanceof PermitDuplicatesModule
522           && ((PermitDuplicatesModule) o).key.equals(key);
523     }
524 
hashCode()525     @Override public int hashCode() {
526       return getClass().hashCode() ^ key.hashCode();
527     }
528   }
529 
checkConfiguration(boolean condition, String format, Object... args)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 
newDuplicateValuesException( Map<T, Binding<T>> existingBindings, Binding<T> binding, final T newValue, Binding<T> duplicateBinding)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 
checkNotNull(T reference, String name)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