• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.google.inject.internal;
2 
3 import static com.google.inject.internal.Element.Type.MULTIBINDER;
4 import static com.google.inject.internal.Errors.checkConfiguration;
5 import static com.google.inject.internal.Errors.checkNotNull;
6 import static com.google.inject.name.Names.named;
7 
8 import com.google.common.base.Objects;
9 import com.google.common.collect.ImmutableList;
10 import com.google.common.collect.ImmutableSet;
11 import com.google.common.collect.Lists;
12 import com.google.common.collect.Sets;
13 import com.google.inject.AbstractModule;
14 import com.google.inject.Binder;
15 import com.google.inject.Binding;
16 import com.google.inject.Injector;
17 import com.google.inject.Key;
18 import com.google.inject.Module;
19 import com.google.inject.Provider;
20 import com.google.inject.TypeLiteral;
21 import com.google.inject.binder.LinkedBindingBuilder;
22 import com.google.inject.internal.InternalProviderInstanceBindingImpl.InitializationTiming;
23 import com.google.inject.multibindings.MultibinderBinding;
24 import com.google.inject.multibindings.MultibindingsTargetVisitor;
25 import com.google.inject.spi.BindingTargetVisitor;
26 import com.google.inject.spi.Dependency;
27 import com.google.inject.spi.ProviderInstanceBinding;
28 import com.google.inject.spi.ProviderWithExtensionVisitor;
29 import com.google.inject.util.Types;
30 import java.lang.reflect.Type;
31 import java.util.Collection;
32 import java.util.List;
33 import java.util.Set;
34 
35 /**
36  * The actual multibinder plays several roles:
37  *
38  * <p>As a Multibinder, it acts as a factory for LinkedBindingBuilders for each of the set's
39  * elements. Each binding is given an annotation that identifies it as a part of this set.
40  *
41  * <p>As a Module, it installs the binding to the set itself. As a module, this implements equals()
42  * and hashcode() in order to trick Guice into executing its configure() method only once. That
43  * makes it so that multiple multibinders can be created for the same target collection, but only
44  * one is bound. Since the list of bindings is retrieved from the injector itself (and not the
45  * multibinder), each multibinder has access to all contributions from all multibinders.
46  *
47  * <p>As a Provider, this constructs the set instances.
48  *
49  * <p>We use a subclass to hide 'implements Module, Provider' from the public API.
50  */
51 public final class RealMultibinder<T> implements Module {
52 
53   /** Implementation of newSetBinder. */
newRealSetBinder(Binder binder, Key<T> key)54   public static <T> RealMultibinder<T> newRealSetBinder(Binder binder, Key<T> key) {
55     binder = binder.skipSources(RealMultibinder.class);
56     RealMultibinder<T> result = new RealMultibinder<>(binder, key);
57     binder.install(result);
58     return result;
59   }
60 
61   @SuppressWarnings("unchecked") // wrapping a T in a Set safely returns a Set<T>
setOf(TypeLiteral<T> elementType)62   static <T> TypeLiteral<Set<T>> setOf(TypeLiteral<T> elementType) {
63     Type type = Types.setOf(elementType.getType());
64     return (TypeLiteral<Set<T>>) TypeLiteral.get(type);
65   }
66 
67   @SuppressWarnings("unchecked")
collectionOfProvidersOf( TypeLiteral<T> elementType)68   static <T> TypeLiteral<Collection<Provider<T>>> collectionOfProvidersOf(
69       TypeLiteral<T> elementType) {
70     Type providerType = Types.providerOf(elementType.getType());
71     Type type = Types.collectionOf(providerType);
72     return (TypeLiteral<Collection<Provider<T>>>) TypeLiteral.get(type);
73   }
74 
75   @SuppressWarnings("unchecked")
collectionOfJavaxProvidersOf( TypeLiteral<T> elementType)76   static <T> TypeLiteral<Collection<javax.inject.Provider<T>>> collectionOfJavaxProvidersOf(
77       TypeLiteral<T> elementType) {
78     Type providerType =
79         Types.newParameterizedType(javax.inject.Provider.class, elementType.getType());
80     Type type = Types.collectionOf(providerType);
81     return (TypeLiteral<Collection<javax.inject.Provider<T>>>) TypeLiteral.get(type);
82   }
83 
84   private final BindingSelection<T> bindingSelection;
85   private final Binder binder;
86 
RealMultibinder(Binder binder, Key<T> key)87   RealMultibinder(Binder binder, Key<T> key) {
88     this.binder = checkNotNull(binder, "binder");
89     this.bindingSelection = new BindingSelection<>(key);
90   }
91 
92   @Override
configure(Binder binder)93   public void configure(Binder binder) {
94     checkConfiguration(!bindingSelection.isInitialized(), "Multibinder was already initialized");
95     binder
96         .bind(bindingSelection.getSetKey())
97         .toProvider(new RealMultibinderProvider<T>(bindingSelection));
98     Provider<Collection<Provider<T>>> collectionOfProvidersProvider =
99         new RealMultibinderCollectionOfProvidersProvider<T>(bindingSelection);
100     binder
101         .bind(bindingSelection.getCollectionOfProvidersKey())
102         .toProvider(collectionOfProvidersProvider);
103 
104     // The collection this exposes is internally an ImmutableList, so it's OK to massage
105     // the guice Provider to javax Provider in the value (since the guice Provider implements
106     // javax Provider).
107     @SuppressWarnings("unchecked")
108     Provider<Collection<javax.inject.Provider<T>>> javaxProvider =
109         (Provider) collectionOfProvidersProvider;
110     binder.bind(bindingSelection.getCollectionOfJavaxProvidersKey()).toProvider(javaxProvider);
111   }
112 
permitDuplicates()113   public void permitDuplicates() {
114     binder.install(new PermitDuplicatesModule(bindingSelection.getPermitDuplicatesKey()));
115   }
116 
117   /** Adds a new entry to the set and returns the key for it. */
getKeyForNewItem()118   Key<T> getKeyForNewItem() {
119     checkConfiguration(!bindingSelection.isInitialized(), "Multibinder was already initialized");
120     return Key.get(
121         bindingSelection.getElementTypeLiteral(),
122         new RealElement(bindingSelection.getSetName(), MULTIBINDER, ""));
123   }
124 
addBinding()125   public LinkedBindingBuilder<T> addBinding() {
126     return binder.bind(getKeyForNewItem());
127   }
128 
129   // These methods are used by RealMapBinder
130 
getSetKey()131   Key<Set<T>> getSetKey() {
132     return bindingSelection.getSetKey();
133   }
134 
getElementTypeLiteral()135   TypeLiteral<T> getElementTypeLiteral() {
136     return bindingSelection.getElementTypeLiteral();
137   }
138 
getSetName()139   String getSetName() {
140     return bindingSelection.getSetName();
141   }
142 
permitsDuplicates(Injector injector)143   boolean permitsDuplicates(Injector injector) {
144     return bindingSelection.permitsDuplicates(injector);
145   }
146 
containsElement(com.google.inject.spi.Element element)147   boolean containsElement(com.google.inject.spi.Element element) {
148     return bindingSelection.containsElement(element);
149   }
150 
151   private static final class RealMultibinderProvider<T>
152       extends InternalProviderInstanceBindingImpl.Factory<Set<T>>
153       implements ProviderWithExtensionVisitor<Set<T>>, MultibinderBinding<Set<T>> {
154     private final BindingSelection<T> bindingSelection;
155     private List<Binding<T>> bindings;
156     private SingleParameterInjector<T>[] injectors;
157     private boolean permitDuplicates;
158 
RealMultibinderProvider(BindingSelection<T> bindingSelection)159     RealMultibinderProvider(BindingSelection<T> bindingSelection) {
160       // While Multibinders only depend on bindings created in modules so we could theoretically
161       // initialize eagerly, they also depend on
162       // 1. findBindingsByType returning results
163       // 2. being able to call BindingImpl.acceptTargetVisitor
164       // neither of those is available during eager initialization, so we use DELAYED
165       super(InitializationTiming.DELAYED);
166       this.bindingSelection = bindingSelection;
167     }
168 
169     @Override
getDependencies()170     public Set<Dependency<?>> getDependencies() {
171       return bindingSelection.getDependencies();
172     }
173 
174     @Override
initialize(InjectorImpl injector, Errors errors)175     void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
176       bindingSelection.initialize(injector, errors);
177       this.bindings = bindingSelection.getBindings();
178       this.injectors = bindingSelection.getParameterInjectors();
179       this.permitDuplicates = bindingSelection.permitsDuplicates();
180     }
181 
182     @Override
doProvision(InternalContext context, Dependency<?> dependency)183     protected Set<T> doProvision(InternalContext context, Dependency<?> dependency)
184         throws InternalProvisionException {
185       SingleParameterInjector<T>[] localInjectors = injectors;
186       if (localInjectors == null) {
187         // if localInjectors == null, then we have no bindings so return the empty set.
188         return ImmutableSet.of();
189       }
190       // Ideally we would just add to an ImmutableSet.Builder, but if we did that and there were
191       // duplicates we wouldn't be able to tell which one was the duplicate.  So to manage this we
192       // first put everything into an array and then construct the set.  This way if something gets
193       // dropped we can figure out what it is.
194       @SuppressWarnings("unchecked")
195       T[] values = (T[]) new Object[localInjectors.length];
196       for (int i = 0; i < localInjectors.length; i++) {
197         SingleParameterInjector<T> parameterInjector = localInjectors[i];
198         T newValue = parameterInjector.inject(context);
199         if (newValue == null) {
200           throw newNullEntryException(i);
201         }
202         values[i] = newValue;
203       }
204       ImmutableSet<T> set = ImmutableSet.copyOf(values);
205       // There are fewer items in the set than the array.  Figure out which one got dropped.
206       if (!permitDuplicates && set.size() < values.length) {
207         throw newDuplicateValuesException(set, values);
208       }
209       return set;
210     }
211 
newNullEntryException(int i)212     private InternalProvisionException newNullEntryException(int i) {
213       return InternalProvisionException.create(
214           "Set injection failed due to null element bound at: %s", bindings.get(i).getSource());
215     }
216 
217     @SuppressWarnings("unchecked")
218     @Override
acceptExtensionVisitor( BindingTargetVisitor<B, V> visitor, ProviderInstanceBinding<? extends B> binding)219     public <B, V> V acceptExtensionVisitor(
220         BindingTargetVisitor<B, V> visitor, ProviderInstanceBinding<? extends B> binding) {
221       if (visitor instanceof MultibindingsTargetVisitor) {
222         return ((MultibindingsTargetVisitor<Set<T>, V>) visitor).visit(this);
223       } else {
224         return visitor.visit(binding);
225       }
226     }
227 
newDuplicateValuesException( ImmutableSet<T> set, T[] values)228     private InternalProvisionException newDuplicateValuesException(
229         ImmutableSet<T> set, T[] values) {
230       // TODO(lukes): consider reporting all duplicate values, the easiest way would be to rebuild
231       // a new set and detect dupes as we go
232       // Find the duplicate binding
233       // To do this we take advantage of the fact that set, values and bindings all have the same
234       // ordering for a non-empty prefix of the set.
235       // First we scan for the first item dropped from the set.
236       int newBindingIndex = 0;
237       for (T item : set) {
238         if (item != values[newBindingIndex]) {
239           break;
240         }
241         newBindingIndex++;
242       }
243       // once we exit the loop newBindingIndex will point at the first item in values that was
244       // dropped.
245 
246       Binding<T> newBinding = bindings.get(newBindingIndex);
247       T newValue = values[newBindingIndex];
248       // Now we scan again to find the index of the value, we are guaranteed to find it.
249       int oldBindingIndex = set.asList().indexOf(newValue);
250       T oldValue = values[oldBindingIndex];
251       Binding<T> duplicateBinding = bindings.get(oldBindingIndex);
252       String oldString = oldValue.toString();
253       String newString = newValue.toString();
254       if (Objects.equal(oldString, newString)) {
255         // When the value strings match, just show the source of the bindings
256         return InternalProvisionException.create(
257             "Set injection failed due to duplicated element \"%s\""
258                 + "\n    Bound at %s\n    Bound at %s",
259             newValue, duplicateBinding.getSource(), newBinding.getSource());
260       } else {
261         // When the value strings don't match, include them both as they may be useful for debugging
262         return InternalProvisionException.create(
263             "Set injection failed due to multiple elements comparing equal:"
264                 + "\n    \"%s\"\n        bound at %s"
265                 + "\n    \"%s\"\n        bound at %s",
266             oldValue, duplicateBinding.getSource(), newValue, newBinding.getSource());
267       }
268     }
269 
270     @Override
equals(Object obj)271     public boolean equals(Object obj) {
272       return obj instanceof RealMultibinderProvider
273           && bindingSelection.equals(((RealMultibinderProvider<?>) obj).bindingSelection);
274     }
275 
276     @Override
hashCode()277     public int hashCode() {
278       return bindingSelection.hashCode();
279     }
280 
281     @Override
getSetKey()282     public Key<Set<T>> getSetKey() {
283       return bindingSelection.getSetKey();
284     }
285 
286     @Override
getElementTypeLiteral()287     public TypeLiteral<?> getElementTypeLiteral() {
288       return bindingSelection.getElementTypeLiteral();
289     }
290 
291     @Override
getElements()292     public List<Binding<?>> getElements() {
293       return bindingSelection.getElements();
294     }
295 
296     @Override
permitsDuplicates()297     public boolean permitsDuplicates() {
298       return bindingSelection.permitsDuplicates();
299     }
300 
301     @Override
containsElement(com.google.inject.spi.Element element)302     public boolean containsElement(com.google.inject.spi.Element element) {
303       return bindingSelection.containsElement(element);
304     }
305   }
306 
307   private static final class BindingSelection<T> {
308     // prior to initialization we declare just a dependency on the injector, but as soon as we are
309     // initialized we swap to dependencies on the elements.
310     private static final ImmutableSet<Dependency<?>> MODULE_DEPENDENCIES =
311         ImmutableSet.<Dependency<?>>of(Dependency.get(Key.get(Injector.class)));
312     private final TypeLiteral<T> elementType;
313     private final Key<Set<T>> setKey;
314 
315     // these are all lazily allocated
316     private String setName;
317     private Key<Collection<Provider<T>>> collectionOfProvidersKey;
318     private Key<Collection<javax.inject.Provider<T>>> collectionOfJavaxProvidersKey;
319     private Key<Boolean> permitDuplicatesKey;
320 
321     private boolean isInitialized;
322     /* a binding for each element in the set. null until initialization, non-null afterwards */
323     private ImmutableList<Binding<T>> bindings;
324 
325     // Starts out as Injector and gets set up properly after initialization
326     private ImmutableSet<Dependency<?>> dependencies = MODULE_DEPENDENCIES;
327     private ImmutableSet<Dependency<?>> providerDependencies = MODULE_DEPENDENCIES;
328 
329     /** whether duplicates are allowed. Possibly configured by a different instance */
330     private boolean permitDuplicates;
331 
332     private SingleParameterInjector<T>[] parameterinjectors;
333 
BindingSelection(Key<T> key)334     BindingSelection(Key<T> key) {
335       this.setKey = key.ofType(setOf(key.getTypeLiteral()));
336       this.elementType = key.getTypeLiteral();
337     }
338 
initialize(InjectorImpl injector, Errors errors)339     void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
340       // This will be called multiple times, once by each Factory. We only want
341       // to do the work to initialize everything once, so guard this code with
342       // isInitialized.
343       if (isInitialized) {
344         return;
345       }
346       List<Binding<T>> bindings = Lists.newArrayList();
347       Set<Indexer.IndexedBinding> index = Sets.newHashSet();
348       Indexer indexer = new Indexer(injector);
349       List<Dependency<?>> dependencies = Lists.newArrayList();
350       List<Dependency<?>> providerDependencies = Lists.newArrayList();
351       for (Binding<?> entry : injector.findBindingsByType(elementType)) {
352         if (keyMatches(entry.getKey())) {
353           @SuppressWarnings("unchecked") // protected by findBindingsByType()
354           Binding<T> binding = (Binding<T>) entry;
355           if (index.add(binding.acceptTargetVisitor(indexer))) {
356             // TODO(lukes): most of these are linked bindings since user bindings are linked to
357             // a user binding through the @Element annotation.  Since this is an implementation
358             // detail we could 'dereference' the @Element if it is a LinkedBinding and avoid
359             // provisioning through the FactoryProxy at runtime.
360             // Ditto for OptionalBinder/MapBinder
361             bindings.add(binding);
362             Key<T> key = binding.getKey();
363             // TODO(lukes): we should mark this as a non-nullable dependency since we don't accept
364             // null.
365             // Add a dependency on Key<T>
366             dependencies.add(Dependency.get(key));
367             // and add a dependency on Key<Provider<T>>
368             providerDependencies.add(
369                 Dependency.get(key.ofType(Types.providerOf(key.getTypeLiteral().getType()))));
370           }
371         }
372       }
373 
374       this.bindings = ImmutableList.copyOf(bindings);
375       this.dependencies = ImmutableSet.copyOf(dependencies);
376       this.providerDependencies = ImmutableSet.copyOf(providerDependencies);
377       this.permitDuplicates = permitsDuplicates(injector);
378       // This is safe because all our dependencies are assignable to T and we never assign to
379       // elements of this array.
380       @SuppressWarnings("unchecked")
381       SingleParameterInjector<T>[] typed =
382           (SingleParameterInjector<T>[]) injector.getParametersInjectors(dependencies, errors);
383       this.parameterinjectors = typed;
384       isInitialized = true;
385     }
386 
permitsDuplicates(Injector injector)387     boolean permitsDuplicates(Injector injector) {
388       return injector.getBindings().containsKey(getPermitDuplicatesKey());
389     }
390 
getBindings()391     ImmutableList<Binding<T>> getBindings() {
392       checkConfiguration(isInitialized, "not initialized");
393       return bindings;
394     }
395 
getParameterInjectors()396     SingleParameterInjector<T>[] getParameterInjectors() {
397       checkConfiguration(isInitialized, "not initialized");
398       return parameterinjectors;
399     }
400 
getDependencies()401     ImmutableSet<Dependency<?>> getDependencies() {
402       return dependencies;
403     }
404 
getProviderDependencies()405     ImmutableSet<Dependency<?>> getProviderDependencies() {
406       return providerDependencies;
407     }
408 
getSetName()409     String getSetName() {
410       // lazily initialized since most selectors don't survive module installation.
411       if (setName == null) {
412         setName = Annotations.nameOf(setKey);
413       }
414       return setName;
415     }
416 
getPermitDuplicatesKey()417     Key<Boolean> getPermitDuplicatesKey() {
418       Key<Boolean> local = permitDuplicatesKey;
419       if (local == null) {
420         local =
421             permitDuplicatesKey = Key.get(Boolean.class, named(toString() + " permits duplicates"));
422       }
423       return local;
424     }
425 
getCollectionOfProvidersKey()426     Key<Collection<Provider<T>>> getCollectionOfProvidersKey() {
427       Key<Collection<Provider<T>>> local = collectionOfProvidersKey;
428       if (local == null) {
429         local = collectionOfProvidersKey = setKey.ofType(collectionOfProvidersOf(elementType));
430       }
431       return local;
432     }
433 
getCollectionOfJavaxProvidersKey()434     Key<Collection<javax.inject.Provider<T>>> getCollectionOfJavaxProvidersKey() {
435       Key<Collection<javax.inject.Provider<T>>> local = collectionOfJavaxProvidersKey;
436       if (local == null) {
437         local =
438             collectionOfJavaxProvidersKey =
439                 setKey.ofType(collectionOfJavaxProvidersOf(elementType));
440       }
441       return local;
442     }
443 
isInitialized()444     boolean isInitialized() {
445       return isInitialized;
446     }
447 
448     // MultibinderBinding API methods
449 
getElementTypeLiteral()450     TypeLiteral<T> getElementTypeLiteral() {
451       return elementType;
452     }
453 
getSetKey()454     Key<Set<T>> getSetKey() {
455       return setKey;
456     }
457 
458     @SuppressWarnings("unchecked")
getElements()459     List<Binding<?>> getElements() {
460       if (isInitialized()) {
461         return (List<Binding<?>>) (List<?>) bindings; // safe because bindings is immutable.
462       } else {
463         throw new UnsupportedOperationException("getElements() not supported for module bindings");
464       }
465     }
466 
permitsDuplicates()467     boolean permitsDuplicates() {
468       if (isInitialized()) {
469         return permitDuplicates;
470       } else {
471         throw new UnsupportedOperationException(
472             "permitsDuplicates() not supported for module bindings");
473       }
474     }
475 
containsElement(com.google.inject.spi.Element element)476     boolean containsElement(com.google.inject.spi.Element element) {
477       if (element instanceof Binding) {
478         Binding<?> binding = (Binding<?>) element;
479         return keyMatches(binding.getKey())
480             || binding.getKey().equals(getPermitDuplicatesKey())
481             || binding.getKey().equals(setKey)
482             || binding.getKey().equals(collectionOfProvidersKey)
483             || binding.getKey().equals(collectionOfJavaxProvidersKey);
484       } else {
485         return false;
486       }
487     }
488 
keyMatches(Key<?> key)489     private boolean keyMatches(Key<?> key) {
490       return key.getTypeLiteral().equals(elementType)
491           && key.getAnnotation() instanceof Element
492           && ((Element) key.getAnnotation()).setName().equals(getSetName())
493           && ((Element) key.getAnnotation()).type() == MULTIBINDER;
494     }
495 
496     @Override
equals(Object obj)497     public boolean equals(Object obj) {
498       if (obj instanceof BindingSelection) {
499         return setKey.equals(((BindingSelection<?>) obj).setKey);
500       }
501       return false;
502     }
503 
504     @Override
hashCode()505     public int hashCode() {
506       return setKey.hashCode();
507     }
508 
509     @Override
toString()510     public String toString() {
511       return (getSetName().isEmpty() ? "" : getSetName() + " ")
512           + "Multibinder<"
513           + elementType
514           + ">";
515     }
516   }
517 
518   @Override
equals(Object o)519   public boolean equals(Object o) {
520     return o instanceof RealMultibinder
521         && ((RealMultibinder<?>) o).bindingSelection.equals(bindingSelection);
522   }
523 
524   @Override
hashCode()525   public int hashCode() {
526     return bindingSelection.hashCode();
527   }
528 
529   private static final class RealMultibinderCollectionOfProvidersProvider<T>
530       extends InternalProviderInstanceBindingImpl.Factory<Collection<Provider<T>>> {
531 
532     private final BindingSelection<T> bindingSelection;
533     private ImmutableList<Provider<T>> collectionOfProviders;
534 
RealMultibinderCollectionOfProvidersProvider(BindingSelection<T> bindingSelection)535     RealMultibinderCollectionOfProvidersProvider(BindingSelection<T> bindingSelection) {
536       super(InitializationTiming.DELAYED); // See comment in RealMultibinderProvider
537       this.bindingSelection = bindingSelection;
538     }
539 
540     @Override
initialize(InjectorImpl injector, Errors errors)541     void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
542       bindingSelection.initialize(injector, errors);
543       ImmutableList.Builder<Provider<T>> providers = ImmutableList.builder();
544       for (Binding<T> binding : bindingSelection.getBindings()) {
545         providers.add(binding.getProvider());
546       }
547       this.collectionOfProviders = providers.build();
548     }
549 
550     @Override
doProvision( InternalContext context, Dependency<?> dependency)551     protected Collection<Provider<T>> doProvision(
552         InternalContext context, Dependency<?> dependency) {
553       return collectionOfProviders;
554     }
555 
556     @Override
getDependencies()557     public Set<Dependency<?>> getDependencies() {
558       return bindingSelection.getProviderDependencies();
559     }
560 
561     @Override
equals(Object obj)562     public boolean equals(Object obj) {
563       return obj instanceof RealMultibinderCollectionOfProvidersProvider
564           && bindingSelection.equals(
565               ((RealMultibinderCollectionOfProvidersProvider<?>) obj).bindingSelection);
566     }
567 
568     @Override
hashCode()569     public int hashCode() {
570       return bindingSelection.hashCode();
571     }
572   }
573 
574   /**
575    * We install the permit duplicates configuration as its own binding, all by itself. This way, if
576    * only one of a multibinder's users remember to call permitDuplicates(), they're still permitted.
577    *
578    * <p>This is like setting a global variable in the injector so that each instance of the
579    * multibinder will have the same value for permitDuplicates, even if it is only set on one of
580    * them.
581    */
582   private static class PermitDuplicatesModule extends AbstractModule {
583     private final Key<Boolean> key;
584 
PermitDuplicatesModule(Key<Boolean> key)585     PermitDuplicatesModule(Key<Boolean> key) {
586       this.key = key;
587     }
588 
589     @Override
configure()590     protected void configure() {
591       bind(key).toInstance(true);
592     }
593 
594     @Override
equals(Object o)595     public boolean equals(Object o) {
596       return o instanceof PermitDuplicatesModule && ((PermitDuplicatesModule) o).key.equals(key);
597     }
598 
599     @Override
hashCode()600     public int hashCode() {
601       return getClass().hashCode() ^ key.hashCode();
602     }
603   }
604 }
605