/**
* Copyright (C) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.inject.multibindings;
import static com.google.inject.multibindings.Element.Type.MAPBINDER;
import static com.google.inject.multibindings.Multibinder.checkConfiguration;
import static com.google.inject.multibindings.Multibinder.checkNotNull;
import static com.google.inject.multibindings.Multibinder.setOf;
import static com.google.inject.util.Types.newParameterizedType;
import static com.google.inject.util.Types.newParameterizedTypeWithOwner;
import com.google.common.base.Joiner;
import com.google.common.base.Objects;
import com.google.common.base.Supplier;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import com.google.common.collect.Sets;
import com.google.inject.Binder;
import com.google.inject.Binding;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Module;
import com.google.inject.Provider;
import com.google.inject.TypeLiteral;
import com.google.inject.binder.LinkedBindingBuilder;
import com.google.inject.internal.Errors;
import com.google.inject.multibindings.Indexer.IndexedBinding;
import com.google.inject.multibindings.Multibinder.RealMultibinder;
import com.google.inject.spi.BindingTargetVisitor;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.Element;
import com.google.inject.spi.HasDependencies;
import com.google.inject.spi.ProviderInstanceBinding;
import com.google.inject.spi.ProviderLookup;
import com.google.inject.spi.ProviderWithDependencies;
import com.google.inject.spi.ProviderWithExtensionVisitor;
import com.google.inject.spi.Toolable;
import com.google.inject.util.Types;
import java.lang.annotation.Annotation;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
/**
* An API to bind multiple map entries separately, only to later inject them as
* a complete map. MapBinder is intended for use in your application's module:
*
* public class SnacksModule extends AbstractModule {
* protected void configure() {
* MapBinder<String, Snack> mapbinder
* = MapBinder.newMapBinder(binder(), String.class, Snack.class);
* mapbinder.addBinding("twix").toInstance(new Twix());
* mapbinder.addBinding("snickers").toProvider(SnickersProvider.class);
* mapbinder.addBinding("skittles").to(Skittles.class);
* }
* }
*
* With this binding, a {@link Map}{@code } can now be
* injected:
*
* class SnackMachine {
* {@literal @}Inject
* public SnackMachine(Map<String, Snack> snacks) { ... }
* }
*
* In addition to binding {@code Map}, a mapbinder will also bind
* {@code Map>} for lazy value provision:
*
* class SnackMachine {
* {@literal @}Inject
* public SnackMachine(Map<String, Provider<Snack>> snackProviders) { ... }
* }
*
* Contributing mapbindings from different modules is supported. For example,
* it is okay to have both {@code CandyModule} and {@code ChipsModule} both
* create their own {@code MapBinder}, and to each contribute
* bindings to the snacks map. When that map is injected, it will contain
* entries from both modules.
*
* The map's iteration order is consistent with the binding order. This is
* convenient when multiple elements are contributed by the same module because
* that module can order its bindings appropriately. Avoid relying on the
* iteration order of elements contributed by different modules, since there is
* no equivalent mechanism to order modules.
*
*
The map is unmodifiable. Elements can only be added to the map by
* configuring the MapBinder. Elements can never be removed from the map.
*
*
Values are resolved at map injection time. If a value is bound to a
* provider, that provider's get method will be called each time the map is
* injected (unless the binding is also scoped, or a map of providers is injected).
*
*
Annotations are used to create different maps of the same key/value
* type. Each distinct annotation gets its own independent map.
*
*
Keys must be distinct. If the same key is bound more than
* once, map injection will fail. However, use {@link #permitDuplicates()} in
* order to allow duplicate keys; extra bindings to {@code Map>} and
* {@code Map>} will be added.
*
* Keys must be non-null. {@code addBinding(null)} will
* throw an unchecked exception.
*
*
Values must be non-null to use map injection. If any
* value is null, map injection will fail (although injecting a map of providers
* will not).
*
* @author dpb@google.com (David P. Baker)
*/
public abstract class MapBinder {
private MapBinder() {}
/**
* Returns a new mapbinder that collects entries of {@code keyType}/{@code valueType} in a
* {@link Map} that is itself bound with no binding annotation.
*/
public static MapBinder newMapBinder(Binder binder,
TypeLiteral keyType, TypeLiteral valueType) {
binder = binder.skipSources(MapBinder.class, RealMapBinder.class);
return newRealMapBinder(binder, keyType, valueType, Key.get(mapOf(keyType, valueType)),
Multibinder.newSetBinder(binder, entryOfProviderOf(keyType, valueType)));
}
/**
* Returns a new mapbinder that collects entries of {@code keyType}/{@code valueType} in a
* {@link Map} that is itself bound with no binding annotation.
*/
public static MapBinder newMapBinder(Binder binder,
Class keyType, Class valueType) {
return newMapBinder(binder, TypeLiteral.get(keyType), TypeLiteral.get(valueType));
}
/**
* Returns a new mapbinder that collects entries of {@code keyType}/{@code valueType} in a
* {@link Map} that is itself bound with {@code annotation}.
*/
public static MapBinder newMapBinder(Binder binder,
TypeLiteral keyType, TypeLiteral valueType, Annotation annotation) {
binder = binder.skipSources(MapBinder.class, RealMapBinder.class);
return newRealMapBinder(binder, keyType, valueType,
Key.get(mapOf(keyType, valueType), annotation),
Multibinder.newSetBinder(binder, entryOfProviderOf(keyType, valueType), annotation));
}
/**
* Returns a new mapbinder that collects entries of {@code keyType}/{@code valueType} in a
* {@link Map} that is itself bound with {@code annotation}.
*/
public static MapBinder newMapBinder(Binder binder,
Class keyType, Class valueType, Annotation annotation) {
return newMapBinder(binder, TypeLiteral.get(keyType), TypeLiteral.get(valueType), annotation);
}
/**
* Returns a new mapbinder that collects entries of {@code keyType}/{@code valueType} in a
* {@link Map} that is itself bound with {@code annotationType}.
*/
public static MapBinder newMapBinder(Binder binder, TypeLiteral keyType,
TypeLiteral valueType, Class extends Annotation> annotationType) {
binder = binder.skipSources(MapBinder.class, RealMapBinder.class);
return newRealMapBinder(binder, keyType, valueType,
Key.get(mapOf(keyType, valueType), annotationType),
Multibinder.newSetBinder(binder, entryOfProviderOf(keyType, valueType), annotationType));
}
/**
* Returns a new mapbinder that collects entries of {@code keyType}/{@code valueType} in a
* {@link Map} that is itself bound with {@code annotationType}.
*/
public static MapBinder newMapBinder(Binder binder, Class keyType,
Class valueType, Class extends Annotation> annotationType) {
return newMapBinder(
binder, TypeLiteral.get(keyType), TypeLiteral.get(valueType), annotationType);
}
@SuppressWarnings("unchecked") // a map of is safely a Map
static TypeLiteral