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<Snack> 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<Snack> 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