• 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.inject.internal.RealMapBinder.newMapRealBinder;
20 import static com.google.inject.internal.RealMapBinder.newRealMapBinder;
21 
22 import com.google.inject.Binder;
23 import com.google.inject.TypeLiteral;
24 import com.google.inject.binder.LinkedBindingBuilder;
25 import com.google.inject.internal.RealMapBinder;
26 import java.lang.annotation.Annotation;
27 import java.util.Map;
28 
29 /**
30  * An API to bind multiple map entries separately, only to later inject them as a complete map.
31  * MapBinder is intended for use in your application's module:
32  *
33  * <pre><code>
34  * public class SnacksModule extends AbstractModule {
35  *   protected void configure() {
36  *     MapBinder&lt;String, Snack&gt; mapbinder
37  *         = MapBinder.newMapBinder(binder(), String.class, Snack.class);
38  *     mapbinder.addBinding("twix").toInstance(new Twix());
39  *     mapbinder.addBinding("snickers").toProvider(SnickersProvider.class);
40  *     mapbinder.addBinding("skittles").to(Skittles.class);
41  *   }
42  * }</code></pre>
43  *
44  * <p>With this binding, a {@link Map}{@code <String, Snack>} can now be injected:
45  *
46  * <pre><code>
47  * class SnackMachine {
48  *   {@literal @}Inject
49  *   public SnackMachine(Map&lt;String, Snack&gt; snacks) { ... }
50  * }</code></pre>
51  *
52  * <p>In addition to binding {@code Map<K, V>}, a mapbinder will also bind {@code Map<K,
53  * Provider<V>>} for lazy value provision:
54  *
55  * <pre><code>
56  * class SnackMachine {
57  *   {@literal @}Inject
58  *   public SnackMachine(Map&lt;String, Provider&lt;Snack&gt;&gt; snackProviders) { ... }
59  * }</code></pre>
60  *
61  * <p>Contributing mapbindings from different modules is supported. For example, it is okay to have
62  * both {@code CandyModule} and {@code ChipsModule} both create their own {@code MapBinder<String,
63  * Snack>}, and to each contribute bindings to the snacks map. When that map is injected, it will
64  * contain entries from both modules.
65  *
66  * <p>The map's iteration order is consistent with the binding order. This is convenient when
67  * multiple elements are contributed by the same module because that module can order its bindings
68  * appropriately. Avoid relying on the iteration order of elements contributed by different modules,
69  * since there is no equivalent mechanism to order modules.
70  *
71  * <p>The map is unmodifiable. Elements can only be added to the map by configuring the MapBinder.
72  * Elements can never be removed from the map.
73  *
74  * <p>Values are resolved at map injection time. If a value is bound to a provider, that provider's
75  * get method will be called each time the map is injected (unless the binding is also scoped, or a
76  * map of providers is injected).
77  *
78  * <p>Annotations are used to create different maps of the same key/value type. Each distinct
79  * annotation gets its own independent map.
80  *
81  * <p><strong>Keys must be distinct.</strong> If the same key is bound more than once, map injection
82  * will fail. However, use {@link #permitDuplicates()} in order to allow duplicate keys; extra
83  * bindings to {@code Map<K, Set<V>>} and {@code Map<K, Set<Provider<V>>} will be added.
84  *
85  * <p><strong>Keys must be non-null.</strong> {@code addBinding(null)} will throw an unchecked
86  * exception.
87  *
88  * <p><strong>Values must be non-null to use map injection.</strong> If any value is null, map
89  * injection will fail (although injecting a map of providers will not).
90  *
91  * @author dpb@google.com (David P. Baker)
92  */
93 public class MapBinder<K, V> {
94   // This class is non-final due to users mocking this in tests :(
95 
96   /**
97    * Returns a new mapbinder that collects entries of {@code keyType}/{@code valueType} in a {@link
98    * Map} that is itself bound with no binding annotation.
99    */
newMapBinder( Binder binder, TypeLiteral<K> keyType, TypeLiteral<V> valueType)100   public static <K, V> MapBinder<K, V> newMapBinder(
101       Binder binder, TypeLiteral<K> keyType, TypeLiteral<V> valueType) {
102     return new MapBinder<K, V>(
103         newMapRealBinder(binder.skipSources(MapBinder.class), keyType, valueType));
104   }
105 
106   /**
107    * Returns a new mapbinder that collects entries of {@code keyType}/{@code valueType} in a {@link
108    * Map} that is itself bound with no binding annotation.
109    */
newMapBinder( Binder binder, Class<K> keyType, Class<V> valueType)110   public static <K, V> MapBinder<K, V> newMapBinder(
111       Binder binder, Class<K> keyType, Class<V> valueType) {
112     return newMapBinder(binder, TypeLiteral.get(keyType), TypeLiteral.get(valueType));
113   }
114 
115   /**
116    * Returns a new mapbinder that collects entries of {@code keyType}/{@code valueType} in a {@link
117    * Map} that is itself bound with {@code annotation}.
118    */
newMapBinder( Binder binder, TypeLiteral<K> keyType, TypeLiteral<V> valueType, Annotation annotation)119   public static <K, V> MapBinder<K, V> newMapBinder(
120       Binder binder, TypeLiteral<K> keyType, TypeLiteral<V> valueType, Annotation annotation) {
121     return new MapBinder<K, V>(
122         newRealMapBinder(binder.skipSources(MapBinder.class), keyType, valueType, annotation));
123   }
124 
125   /**
126    * Returns a new mapbinder that collects entries of {@code keyType}/{@code valueType} in a {@link
127    * Map} that is itself bound with {@code annotation}.
128    */
newMapBinder( Binder binder, Class<K> keyType, Class<V> valueType, Annotation annotation)129   public static <K, V> MapBinder<K, V> newMapBinder(
130       Binder binder, Class<K> keyType, Class<V> valueType, Annotation annotation) {
131     return newMapBinder(binder, TypeLiteral.get(keyType), TypeLiteral.get(valueType), annotation);
132   }
133 
134   /**
135    * Returns a new mapbinder that collects entries of {@code keyType}/{@code valueType} in a {@link
136    * Map} that is itself bound with {@code annotationType}.
137    */
newMapBinder( Binder binder, TypeLiteral<K> keyType, TypeLiteral<V> valueType, Class<? extends Annotation> annotationType)138   public static <K, V> MapBinder<K, V> newMapBinder(
139       Binder binder,
140       TypeLiteral<K> keyType,
141       TypeLiteral<V> valueType,
142       Class<? extends Annotation> annotationType) {
143     return new MapBinder<K, V>(
144         newRealMapBinder(binder.skipSources(MapBinder.class), keyType, valueType, annotationType));
145   }
146 
147   /**
148    * Returns a new mapbinder that collects entries of {@code keyType}/{@code valueType} in a {@link
149    * Map} that is itself bound with {@code annotationType}.
150    */
newMapBinder( Binder binder, Class<K> keyType, Class<V> valueType, Class<? extends Annotation> annotationType)151   public static <K, V> MapBinder<K, V> newMapBinder(
152       Binder binder,
153       Class<K> keyType,
154       Class<V> valueType,
155       Class<? extends Annotation> annotationType) {
156     return newMapBinder(
157         binder, TypeLiteral.get(keyType), TypeLiteral.get(valueType), annotationType);
158   }
159 
160   private final RealMapBinder<K, V> delegate;
161 
MapBinder(RealMapBinder<K, V> delegate)162   private MapBinder(RealMapBinder<K, V> delegate) {
163     this.delegate = delegate;
164   }
165 
166   /**
167    * Configures the {@code MapBinder} to handle duplicate entries.
168    *
169    * <p>When multiple equal keys are bound, the value that gets included in the map is arbitrary.
170    *
171    * <p>In addition to the {@code Map<K, V>} and {@code Map<K, Provider<V>>} maps that are normally
172    * bound, a {@code Map<K, Set<V>>} and {@code Map<K, Set<Provider<V>>>} are <em>also</em> bound,
173    * which contain all values bound to each key.
174    *
175    * <p>When multiple modules contribute elements to the map, this configuration option impacts all
176    * of them.
177    *
178    * @return this map binder
179    * @since 3.0
180    */
permitDuplicates()181   public MapBinder<K, V> permitDuplicates() {
182     delegate.permitDuplicates();
183     return this;
184   }
185 
186   /**
187    * Returns a binding builder used to add a new entry in the map. Each key must be distinct (and
188    * non-null). Bound providers will be evaluated each time the map is injected.
189    *
190    * <p>It is an error to call this method without also calling one of the {@code to} methods on the
191    * returned binding builder.
192    *
193    * <p>Scoping elements independently is supported. Use the {@code in} method to specify a binding
194    * scope.
195    */
addBinding(K key)196   public LinkedBindingBuilder<V> addBinding(K key) {
197     return delegate.addBinding(key);
198   }
199 
200   // Some tests rely on MapBinder implementing equals/hashCode
201 
202   @Override
equals(Object obj)203   public boolean equals(Object obj) {
204     if (obj instanceof MapBinder) {
205       return delegate.equals(((MapBinder<?, ?>) obj).delegate);
206     }
207     return false;
208   }
209 
210   @Override
hashCode()211   public int hashCode() {
212     return delegate.hashCode();
213   }
214 }
215