limpbizkit | c75363a | 2008-05-13 07:16:26 +0000 | [diff] [blame] | 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 | |
Sam Berlin | 21a1967 | 2012-01-21 11:06:49 -0500 | [diff] [blame] | 19 | import static com.google.inject.multibindings.Element.Type.MAPBINDER; |
sberlin | b7a02b0 | 2011-07-08 00:34:16 +0000 | [diff] [blame] | 20 | import static com.google.inject.multibindings.Multibinder.checkConfiguration; |
| 21 | import static com.google.inject.multibindings.Multibinder.checkNotNull; |
| 22 | import static com.google.inject.multibindings.Multibinder.setOf; |
Sam Berlin | 3338a48 | 2013-12-06 17:07:34 -0500 | [diff] [blame] | 23 | import static com.google.inject.util.Types.newParameterizedType; |
sberlin | b7a02b0 | 2011-07-08 00:34:16 +0000 | [diff] [blame] | 24 | import static com.google.inject.util.Types.newParameterizedTypeWithOwner; |
| 25 | |
Sam Berlin | 8859456 | 2014-03-10 13:58:41 -0400 | [diff] [blame] | 26 | import com.google.common.base.Joiner; |
Sam Berlin | c34e018 | 2014-08-06 11:56:26 -0400 | [diff] [blame] | 27 | import com.google.common.base.Objects; |
| 28 | import com.google.common.base.Supplier; |
| 29 | import com.google.common.collect.HashMultimap; |
sberlin | b7a02b0 | 2011-07-08 00:34:16 +0000 | [diff] [blame] | 30 | import com.google.common.collect.ImmutableList; |
| 31 | import com.google.common.collect.ImmutableMap; |
| 32 | import com.google.common.collect.ImmutableSet; |
| 33 | import com.google.common.collect.Lists; |
Christian Edward Gruber | 6c69bcf | 2013-09-18 10:55:21 -0700 | [diff] [blame] | 34 | import com.google.common.collect.Maps; |
Sam Berlin | 8859456 | 2014-03-10 13:58:41 -0400 | [diff] [blame] | 35 | import com.google.common.collect.Multimap; |
Christian Edward Gruber | d1d7ef3 | 2014-04-01 15:10:51 -0700 | [diff] [blame] | 36 | import com.google.common.collect.Multimaps; |
Christian Edward Gruber | cade897 | 2014-04-01 15:07:02 -0700 | [diff] [blame] | 37 | import com.google.common.collect.Sets; |
limpbizkit | 9532e62 | 2008-06-18 08:20:54 +0000 | [diff] [blame] | 38 | import com.google.inject.Binder; |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 39 | import com.google.inject.Binding; |
limpbizkit | 9532e62 | 2008-06-18 08:20:54 +0000 | [diff] [blame] | 40 | import com.google.inject.Inject; |
limpbizkit | 398017a | 2009-06-23 06:30:37 +0000 | [diff] [blame] | 41 | import com.google.inject.Injector; |
limpbizkit | 9532e62 | 2008-06-18 08:20:54 +0000 | [diff] [blame] | 42 | import com.google.inject.Key; |
| 43 | import com.google.inject.Module; |
| 44 | import com.google.inject.Provider; |
| 45 | import com.google.inject.TypeLiteral; |
limpbizkit | c75363a | 2008-05-13 07:16:26 +0000 | [diff] [blame] | 46 | import com.google.inject.binder.LinkedBindingBuilder; |
Christian Edward Gruber | cade897 | 2014-04-01 15:07:02 -0700 | [diff] [blame] | 47 | import com.google.inject.internal.Errors; |
Sam Berlin | c34e018 | 2014-08-06 11:56:26 -0400 | [diff] [blame] | 48 | import com.google.inject.multibindings.Indexer.IndexedBinding; |
limpbizkit | c75363a | 2008-05-13 07:16:26 +0000 | [diff] [blame] | 49 | import com.google.inject.multibindings.Multibinder.RealMultibinder; |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 50 | import com.google.inject.spi.BindingTargetVisitor; |
limpbizkit | ddb3862 | 2008-12-29 05:21:16 +0000 | [diff] [blame] | 51 | import com.google.inject.spi.Dependency; |
Sam Berlin | c34e018 | 2014-08-06 11:56:26 -0400 | [diff] [blame] | 52 | import com.google.inject.spi.Element; |
Christian Edward Gruber | cade897 | 2014-04-01 15:07:02 -0700 | [diff] [blame] | 53 | import com.google.inject.spi.HasDependencies; |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 54 | import com.google.inject.spi.ProviderInstanceBinding; |
| 55 | import com.google.inject.spi.ProviderLookup; |
limpbizkit | ddb3862 | 2008-12-29 05:21:16 +0000 | [diff] [blame] | 56 | import com.google.inject.spi.ProviderWithDependencies; |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 57 | import com.google.inject.spi.ProviderWithExtensionVisitor; |
| 58 | import com.google.inject.spi.Toolable; |
limpbizkit | 9532e62 | 2008-06-18 08:20:54 +0000 | [diff] [blame] | 59 | import com.google.inject.util.Types; |
sberlin | b7a02b0 | 2011-07-08 00:34:16 +0000 | [diff] [blame] | 60 | |
Christian Edward Gruber | 6c69bcf | 2013-09-18 10:55:21 -0700 | [diff] [blame] | 61 | import java.lang.annotation.Annotation; |
Christian Edward Gruber | d1d7ef3 | 2014-04-01 15:10:51 -0700 | [diff] [blame] | 62 | import java.util.Collection; |
Christian Edward Gruber | 6c69bcf | 2013-09-18 10:55:21 -0700 | [diff] [blame] | 63 | import java.util.Collections; |
| 64 | import java.util.LinkedHashMap; |
| 65 | import java.util.List; |
| 66 | import java.util.Map; |
| 67 | import java.util.Map.Entry; |
| 68 | import java.util.Set; |
| 69 | |
limpbizkit | c75363a | 2008-05-13 07:16:26 +0000 | [diff] [blame] | 70 | /** |
| 71 | * An API to bind multiple map entries separately, only to later inject them as |
| 72 | * a complete map. MapBinder is intended for use in your application's module: |
| 73 | * <pre><code> |
| 74 | * public class SnacksModule extends AbstractModule { |
| 75 | * protected void configure() { |
| 76 | * MapBinder<String, Snack> mapbinder |
| 77 | * = MapBinder.newMapBinder(binder(), String.class, Snack.class); |
| 78 | * mapbinder.addBinding("twix").toInstance(new Twix()); |
| 79 | * mapbinder.addBinding("snickers").toProvider(SnickersProvider.class); |
| 80 | * mapbinder.addBinding("skittles").to(Skittles.class); |
| 81 | * } |
| 82 | * }</code></pre> |
| 83 | * |
netdpb | 0efcc6c | 2009-07-07 20:13:32 +0000 | [diff] [blame] | 84 | * <p>With this binding, a {@link Map}{@code <String, Snack>} can now be |
limpbizkit | c75363a | 2008-05-13 07:16:26 +0000 | [diff] [blame] | 85 | * injected: |
| 86 | * <pre><code> |
| 87 | * class SnackMachine { |
| 88 | * {@literal @}Inject |
| 89 | * public SnackMachine(Map<String, Snack> snacks) { ... } |
| 90 | * }</code></pre> |
netdpb | 0efcc6c | 2009-07-07 20:13:32 +0000 | [diff] [blame] | 91 | * |
limpbizkit | c75363a | 2008-05-13 07:16:26 +0000 | [diff] [blame] | 92 | * <p>In addition to binding {@code Map<K, V>}, a mapbinder will also bind |
| 93 | * {@code Map<K, Provider<V>>} for lazy value provision: |
| 94 | * <pre><code> |
| 95 | * class SnackMachine { |
| 96 | * {@literal @}Inject |
| 97 | * public SnackMachine(Map<String, Provider<Snack>> snackProviders) { ... } |
| 98 | * }</code></pre> |
| 99 | * |
limpbizkit | 0e23eb0 | 2009-10-12 16:17:34 +0000 | [diff] [blame] | 100 | * <p>Contributing mapbindings from different modules is supported. For example, |
| 101 | * it is okay to have both {@code CandyModule} and {@code ChipsModule} both |
netdpb | 0efcc6c | 2009-07-07 20:13:32 +0000 | [diff] [blame] | 102 | * create their own {@code MapBinder<String, Snack>}, and to each contribute |
| 103 | * bindings to the snacks map. When that map is injected, it will contain |
limpbizkit | c75363a | 2008-05-13 07:16:26 +0000 | [diff] [blame] | 104 | * entries from both modules. |
| 105 | * |
limpbizkit | 0e23eb0 | 2009-10-12 16:17:34 +0000 | [diff] [blame] | 106 | * <p>The map's iteration order is consistent with the binding order. This is |
| 107 | * convenient when multiple elements are contributed by the same module because |
| 108 | * that module can order its bindings appropriately. Avoid relying on the |
| 109 | * iteration order of elements contributed by different modules, since there is |
| 110 | * no equivalent mechanism to order modules. |
Christian Edward Gruber | 96e81ba | 2013-09-18 10:55:21 -0700 | [diff] [blame] | 111 | * |
sberlin | 6542fdb | 2010-10-25 03:47:17 +0000 | [diff] [blame] | 112 | * <p>The map is unmodifiable. Elements can only be added to the map by |
| 113 | * configuring the MapBinder. Elements can never be removed from the map. |
limpbizkit | 0e23eb0 | 2009-10-12 16:17:34 +0000 | [diff] [blame] | 114 | * |
limpbizkit | c75363a | 2008-05-13 07:16:26 +0000 | [diff] [blame] | 115 | * <p>Values are resolved at map injection time. If a value is bound to a |
| 116 | * provider, that provider's get method will be called each time the map is |
limpbizkit | 4994bf6 | 2008-12-27 02:54:59 +0000 | [diff] [blame] | 117 | * injected (unless the binding is also scoped, or a map of providers is injected). |
limpbizkit | c75363a | 2008-05-13 07:16:26 +0000 | [diff] [blame] | 118 | * |
limpbizkit | 4994bf6 | 2008-12-27 02:54:59 +0000 | [diff] [blame] | 119 | * <p>Annotations are used to create different maps of the same key/value |
limpbizkit | c75363a | 2008-05-13 07:16:26 +0000 | [diff] [blame] | 120 | * type. Each distinct annotation gets its own independent map. |
| 121 | * |
| 122 | * <p><strong>Keys must be distinct.</strong> If the same key is bound more than |
netdpb | 0efcc6c | 2009-07-07 20:13:32 +0000 | [diff] [blame] | 123 | * once, map injection will fail. However, use {@link #permitDuplicates()} in |
| 124 | * order to allow duplicate keys; extra bindings to {@code Map<K, Set<V>>} and |
| 125 | * {@code Map<K, Set<Provider<V>>} will be added. |
limpbizkit | c75363a | 2008-05-13 07:16:26 +0000 | [diff] [blame] | 126 | * |
netdpb | 0efcc6c | 2009-07-07 20:13:32 +0000 | [diff] [blame] | 127 | * <p><strong>Keys must be non-null.</strong> {@code addBinding(null)} will |
limpbizkit | c75363a | 2008-05-13 07:16:26 +0000 | [diff] [blame] | 128 | * throw an unchecked exception. |
| 129 | * |
| 130 | * <p><strong>Values must be non-null to use map injection.</strong> If any |
| 131 | * value is null, map injection will fail (although injecting a map of providers |
| 132 | * will not). |
| 133 | * |
| 134 | * @author dpb@google.com (David P. Baker) |
limpbizkit | c75363a | 2008-05-13 07:16:26 +0000 | [diff] [blame] | 135 | */ |
| 136 | public abstract class MapBinder<K, V> { |
| 137 | private MapBinder() {} |
limpbizkit | 9532e62 | 2008-06-18 08:20:54 +0000 | [diff] [blame] | 138 | |
limpbizkit | c75363a | 2008-05-13 07:16:26 +0000 | [diff] [blame] | 139 | /** |
limpbizkit | cfa95ae | 2008-11-30 19:52:02 +0000 | [diff] [blame] | 140 | * Returns a new mapbinder that collects entries of {@code keyType}/{@code valueType} in a |
| 141 | * {@link Map} that is itself bound with no binding annotation. |
limpbizkit | c75363a | 2008-05-13 07:16:26 +0000 | [diff] [blame] | 142 | */ |
netdpb | 0efcc6c | 2009-07-07 20:13:32 +0000 | [diff] [blame] | 143 | public static <K, V> MapBinder<K, V> newMapBinder(Binder binder, |
limpbizkit | cfa95ae | 2008-11-30 19:52:02 +0000 | [diff] [blame] | 144 | TypeLiteral<K> keyType, TypeLiteral<V> valueType) { |
limpbizkit | 6663d02 | 2008-06-19 07:57:55 +0000 | [diff] [blame] | 145 | binder = binder.skipSources(MapBinder.class, RealMapBinder.class); |
flan | f618960 | 2014-11-04 07:32:47 -0800 | [diff] [blame] | 146 | return newMapBinder(binder, keyType, valueType, Key.get(mapOf(keyType, valueType)), |
limpbizkit | cfa95ae | 2008-11-30 19:52:02 +0000 | [diff] [blame] | 147 | Multibinder.newSetBinder(binder, entryOfProviderOf(keyType, valueType))); |
limpbizkit | c75363a | 2008-05-13 07:16:26 +0000 | [diff] [blame] | 148 | } |
| 149 | |
| 150 | /** |
limpbizkit | cfa95ae | 2008-11-30 19:52:02 +0000 | [diff] [blame] | 151 | * Returns a new mapbinder that collects entries of {@code keyType}/{@code valueType} in a |
| 152 | * {@link Map} that is itself bound with no binding annotation. |
limpbizkit | c75363a | 2008-05-13 07:16:26 +0000 | [diff] [blame] | 153 | */ |
limpbizkit | cfa95ae | 2008-11-30 19:52:02 +0000 | [diff] [blame] | 154 | public static <K, V> MapBinder<K, V> newMapBinder(Binder binder, |
| 155 | Class<K> keyType, Class<V> valueType) { |
| 156 | return newMapBinder(binder, TypeLiteral.get(keyType), TypeLiteral.get(valueType)); |
limpbizkit | c75363a | 2008-05-13 07:16:26 +0000 | [diff] [blame] | 157 | } |
| 158 | |
| 159 | /** |
limpbizkit | cfa95ae | 2008-11-30 19:52:02 +0000 | [diff] [blame] | 160 | * Returns a new mapbinder that collects entries of {@code keyType}/{@code valueType} in a |
| 161 | * {@link Map} that is itself bound with {@code annotation}. |
limpbizkit | c75363a | 2008-05-13 07:16:26 +0000 | [diff] [blame] | 162 | */ |
netdpb | 0efcc6c | 2009-07-07 20:13:32 +0000 | [diff] [blame] | 163 | public static <K, V> MapBinder<K, V> newMapBinder(Binder binder, |
limpbizkit | cfa95ae | 2008-11-30 19:52:02 +0000 | [diff] [blame] | 164 | TypeLiteral<K> keyType, TypeLiteral<V> valueType, Annotation annotation) { |
limpbizkit | 6663d02 | 2008-06-19 07:57:55 +0000 | [diff] [blame] | 165 | binder = binder.skipSources(MapBinder.class, RealMapBinder.class); |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 166 | return newMapBinder(binder, keyType, valueType, |
limpbizkit | cfa95ae | 2008-11-30 19:52:02 +0000 | [diff] [blame] | 167 | Key.get(mapOf(keyType, valueType), annotation), |
limpbizkit | cfa95ae | 2008-11-30 19:52:02 +0000 | [diff] [blame] | 168 | Multibinder.newSetBinder(binder, entryOfProviderOf(keyType, valueType), annotation)); |
limpbizkit | c75363a | 2008-05-13 07:16:26 +0000 | [diff] [blame] | 169 | } |
| 170 | |
limpbizkit | cfa95ae | 2008-11-30 19:52:02 +0000 | [diff] [blame] | 171 | /** |
| 172 | * Returns a new mapbinder that collects entries of {@code keyType}/{@code valueType} in a |
| 173 | * {@link Map} that is itself bound with {@code annotation}. |
| 174 | */ |
| 175 | public static <K, V> MapBinder<K, V> newMapBinder(Binder binder, |
| 176 | Class<K> keyType, Class<V> valueType, Annotation annotation) { |
| 177 | return newMapBinder(binder, TypeLiteral.get(keyType), TypeLiteral.get(valueType), annotation); |
limpbizkit | c75363a | 2008-05-13 07:16:26 +0000 | [diff] [blame] | 178 | } |
| 179 | |
limpbizkit | cfa95ae | 2008-11-30 19:52:02 +0000 | [diff] [blame] | 180 | /** |
| 181 | * Returns a new mapbinder that collects entries of {@code keyType}/{@code valueType} in a |
| 182 | * {@link Map} that is itself bound with {@code annotationType}. |
| 183 | */ |
| 184 | public static <K, V> MapBinder<K, V> newMapBinder(Binder binder, TypeLiteral<K> keyType, |
| 185 | TypeLiteral<V> valueType, Class<? extends Annotation> annotationType) { |
| 186 | binder = binder.skipSources(MapBinder.class, RealMapBinder.class); |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 187 | return newMapBinder(binder, keyType, valueType, |
limpbizkit | cfa95ae | 2008-11-30 19:52:02 +0000 | [diff] [blame] | 188 | Key.get(mapOf(keyType, valueType), annotationType), |
limpbizkit | cfa95ae | 2008-11-30 19:52:02 +0000 | [diff] [blame] | 189 | Multibinder.newSetBinder(binder, entryOfProviderOf(keyType, valueType), annotationType)); |
| 190 | } |
| 191 | |
| 192 | /** |
| 193 | * Returns a new mapbinder that collects entries of {@code keyType}/{@code valueType} in a |
| 194 | * {@link Map} that is itself bound with {@code annotationType}. |
| 195 | */ |
| 196 | public static <K, V> MapBinder<K, V> newMapBinder(Binder binder, Class<K> keyType, |
| 197 | Class<V> valueType, Class<? extends Annotation> annotationType) { |
| 198 | return newMapBinder( |
| 199 | binder, TypeLiteral.get(keyType), TypeLiteral.get(valueType), annotationType); |
| 200 | } |
| 201 | |
| 202 | @SuppressWarnings("unchecked") // a map of <K, V> is safely a Map<K, V> |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 203 | static <K, V> TypeLiteral<Map<K, V>> mapOf( |
limpbizkit | cfa95ae | 2008-11-30 19:52:02 +0000 | [diff] [blame] | 204 | TypeLiteral<K> keyType, TypeLiteral<V> valueType) { |
| 205 | return (TypeLiteral<Map<K, V>>) TypeLiteral.get( |
| 206 | Types.mapOf(keyType.getType(), valueType.getType())); |
| 207 | } |
| 208 | |
| 209 | @SuppressWarnings("unchecked") // a provider map <K, V> is safely a Map<K, Provider<V>> |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 210 | static <K, V> TypeLiteral<Map<K, Provider<V>>> mapOfProviderOf( |
limpbizkit | cfa95ae | 2008-11-30 19:52:02 +0000 | [diff] [blame] | 211 | TypeLiteral<K> keyType, TypeLiteral<V> valueType) { |
| 212 | return (TypeLiteral<Map<K, Provider<V>>>) TypeLiteral.get( |
netdpb | fb3a896 | 2009-07-08 19:49:54 +0000 | [diff] [blame] | 213 | Types.mapOf(keyType.getType(), Types.providerOf(valueType.getType()))); |
limpbizkit | c75363a | 2008-05-13 07:16:26 +0000 | [diff] [blame] | 214 | } |
Sam Berlin | 3338a48 | 2013-12-06 17:07:34 -0500 | [diff] [blame] | 215 | |
| 216 | // provider map <K, V> is safely a Map<K, javax.inject.Provider<V>>> |
| 217 | @SuppressWarnings("unchecked") |
| 218 | static <K, V> TypeLiteral<Map<K, javax.inject.Provider<V>>> mapOfJavaxProviderOf( |
| 219 | TypeLiteral<K> keyType, TypeLiteral<V> valueType) { |
| 220 | return (TypeLiteral<Map<K, javax.inject.Provider<V>>>) TypeLiteral.get( |
| 221 | Types.mapOf(keyType.getType(), |
| 222 | newParameterizedType(javax.inject.Provider.class, valueType.getType()))); |
| 223 | } |
netdpb | 0efcc6c | 2009-07-07 20:13:32 +0000 | [diff] [blame] | 224 | |
| 225 | @SuppressWarnings("unchecked") // a provider map <K, Set<V>> is safely a Map<K, Set<Provider<V>>> |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 226 | static <K, V> TypeLiteral<Map<K, Set<Provider<V>>>> mapOfSetOfProviderOf( |
netdpb | 0efcc6c | 2009-07-07 20:13:32 +0000 | [diff] [blame] | 227 | TypeLiteral<K> keyType, TypeLiteral<V> valueType) { |
| 228 | return (TypeLiteral<Map<K, Set<Provider<V>>>>) TypeLiteral.get( |
netdpb | fb3a896 | 2009-07-08 19:49:54 +0000 | [diff] [blame] | 229 | Types.mapOf(keyType.getType(), Types.setOf(Types.providerOf(valueType.getType())))); |
netdpb | 0efcc6c | 2009-07-07 20:13:32 +0000 | [diff] [blame] | 230 | } |
| 231 | |
limpbizkit | cfa95ae | 2008-11-30 19:52:02 +0000 | [diff] [blame] | 232 | @SuppressWarnings("unchecked") // a provider entry <K, V> is safely a Map.Entry<K, Provider<V>> |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 233 | static <K, V> TypeLiteral<Map.Entry<K, Provider<V>>> entryOfProviderOf( |
limpbizkit | cfa95ae | 2008-11-30 19:52:02 +0000 | [diff] [blame] | 234 | TypeLiteral<K> keyType, TypeLiteral<V> valueType) { |
| 235 | return (TypeLiteral<Entry<K, Provider<V>>>) TypeLiteral.get(newParameterizedTypeWithOwner( |
| 236 | Map.class, Entry.class, keyType.getType(), Types.providerOf(valueType.getType()))); |
limpbizkit | e97e15b | 2008-05-16 14:53:49 +0000 | [diff] [blame] | 237 | } |
limpbizkit | c75363a | 2008-05-13 07:16:26 +0000 | [diff] [blame] | 238 | |
Christian Edward Gruber | 96e81ba | 2013-09-18 10:55:21 -0700 | [diff] [blame] | 239 | private static <K, V> MapBinder<K, V> newMapBinder(Binder binder, |
flan | f618960 | 2014-11-04 07:32:47 -0800 | [diff] [blame] | 240 | TypeLiteral<K> keyType, TypeLiteral<V> valueType, Key<Map<K, V>> mapKey, |
limpbizkit | e97e15b | 2008-05-16 14:53:49 +0000 | [diff] [blame] | 241 | Multibinder<Entry<K, Provider<V>>> entrySetBinder) { |
flan | f618960 | 2014-11-04 07:32:47 -0800 | [diff] [blame] | 242 | RealMapBinder<K, V> mapBinder = |
| 243 | new RealMapBinder<K, V>(binder, keyType, valueType, mapKey, entrySetBinder); |
limpbizkit | 6663d02 | 2008-06-19 07:57:55 +0000 | [diff] [blame] | 244 | binder.install(mapBinder); |
limpbizkit | e97e15b | 2008-05-16 14:53:49 +0000 | [diff] [blame] | 245 | return mapBinder; |
limpbizkit | c75363a | 2008-05-13 07:16:26 +0000 | [diff] [blame] | 246 | } |
| 247 | |
| 248 | /** |
netdpb | 0efcc6c | 2009-07-07 20:13:32 +0000 | [diff] [blame] | 249 | * Configures the {@code MapBinder} to handle duplicate entries. |
netdpb | fb3a896 | 2009-07-08 19:49:54 +0000 | [diff] [blame] | 250 | * <p>When multiple equal keys are bound, the value that gets included in the map is |
| 251 | * arbitrary. |
| 252 | * <p>In addition to the {@code Map<K, V>} and {@code Map<K, Provider<V>>} |
netdpb | 0efcc6c | 2009-07-07 20:13:32 +0000 | [diff] [blame] | 253 | * maps that are normally bound, a {@code Map<K, Set<V>>} and |
| 254 | * {@code Map<K, Set<Provider<V>>>} are <em>also</em> bound, which contain |
netdpb | fb3a896 | 2009-07-08 19:49:54 +0000 | [diff] [blame] | 255 | * all values bound to each key. |
netdpb | 0efcc6c | 2009-07-07 20:13:32 +0000 | [diff] [blame] | 256 | * <p> |
| 257 | * When multiple modules contribute elements to the map, this configuration |
| 258 | * option impacts all of them. |
limpbizkit | 398017a | 2009-06-23 06:30:37 +0000 | [diff] [blame] | 259 | * |
| 260 | * @return this map binder |
sberlin | c13b545 | 2010-10-31 18:38:24 +0000 | [diff] [blame] | 261 | * @since 3.0 |
limpbizkit | 398017a | 2009-06-23 06:30:37 +0000 | [diff] [blame] | 262 | */ |
| 263 | public abstract MapBinder<K, V> permitDuplicates(); |
| 264 | |
| 265 | /** |
limpbizkit | c75363a | 2008-05-13 07:16:26 +0000 | [diff] [blame] | 266 | * Returns a binding builder used to add a new entry in the map. Each |
| 267 | * key must be distinct (and non-null). Bound providers will be evaluated each |
| 268 | * time the map is injected. |
| 269 | * |
| 270 | * <p>It is an error to call this method without also calling one of the |
| 271 | * {@code to} methods on the returned binding builder. |
| 272 | * |
| 273 | * <p>Scoping elements independently is supported. Use the {@code in} method |
| 274 | * to specify a binding scope. |
| 275 | */ |
| 276 | public abstract LinkedBindingBuilder<V> addBinding(K key); |
| 277 | |
| 278 | /** |
| 279 | * The actual mapbinder plays several roles: |
| 280 | * |
| 281 | * <p>As a MapBinder, it acts as a factory for LinkedBindingBuilders for |
| 282 | * each of the map's values. It delegates to a {@link Multibinder} of |
| 283 | * entries (keys to value providers). |
| 284 | * |
| 285 | * <p>As a Module, it installs the binding to the map itself, as well as to |
netdpb | 0efcc6c | 2009-07-07 20:13:32 +0000 | [diff] [blame] | 286 | * a corresponding map whose values are providers. It uses the entry set |
limpbizkit | c75363a | 2008-05-13 07:16:26 +0000 | [diff] [blame] | 287 | * multibinder to construct the map and the provider map. |
netdpb | 0efcc6c | 2009-07-07 20:13:32 +0000 | [diff] [blame] | 288 | * |
| 289 | * <p>As a module, this implements equals() and hashcode() in order to trick |
| 290 | * Guice into executing its configure() method only once. That makes it so |
limpbizkit | c75363a | 2008-05-13 07:16:26 +0000 | [diff] [blame] | 291 | * that multiple mapbinders can be created for the same target map, but |
| 292 | * only one is bound. Since the list of bindings is retrieved from the |
| 293 | * injector itself (and not the mapbinder), each mapbinder has access to |
| 294 | * all contributions from all equivalent mapbinders. |
| 295 | * |
| 296 | * <p>Rather than binding a single Map.Entry<K, V>, the map binder |
| 297 | * binds keys and values independently. This allows the values to be properly |
| 298 | * scoped. |
| 299 | * |
| 300 | * <p>We use a subclass to hide 'implements Module' from the public API. |
| 301 | */ |
Sam Berlin | 8859456 | 2014-03-10 13:58:41 -0400 | [diff] [blame] | 302 | static final class RealMapBinder<K, V> extends MapBinder<K, V> implements Module { |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 303 | private final TypeLiteral<K> keyType; |
limpbizkit | cfa95ae | 2008-11-30 19:52:02 +0000 | [diff] [blame] | 304 | private final TypeLiteral<V> valueType; |
limpbizkit | c75363a | 2008-05-13 07:16:26 +0000 | [diff] [blame] | 305 | private final Key<Map<K, V>> mapKey; |
Sam Berlin | 3338a48 | 2013-12-06 17:07:34 -0500 | [diff] [blame] | 306 | private final Key<Map<K, javax.inject.Provider<V>>> javaxProviderMapKey; |
limpbizkit | c75363a | 2008-05-13 07:16:26 +0000 | [diff] [blame] | 307 | private final Key<Map<K, Provider<V>>> providerMapKey; |
netdpb | 0efcc6c | 2009-07-07 20:13:32 +0000 | [diff] [blame] | 308 | private final Key<Map<K, Set<V>>> multimapKey; |
| 309 | private final Key<Map<K, Set<Provider<V>>>> providerMultimapKey; |
limpbizkit | e97e15b | 2008-05-16 14:53:49 +0000 | [diff] [blame] | 310 | private final RealMultibinder<Map.Entry<K, Provider<V>>> entrySetBinder; |
Christian Edward Gruber | cade897 | 2014-04-01 15:07:02 -0700 | [diff] [blame] | 311 | private final Map<K, String> duplicateKeyErrorMessages; |
limpbizkit | 9532e62 | 2008-06-18 08:20:54 +0000 | [diff] [blame] | 312 | |
limpbizkit | e97e15b | 2008-05-16 14:53:49 +0000 | [diff] [blame] | 313 | /* the target injector's binder. non-null until initialization, null afterwards */ |
| 314 | private Binder binder; |
Christian Edward Gruber | 96e81ba | 2013-09-18 10:55:21 -0700 | [diff] [blame] | 315 | |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 316 | private boolean permitDuplicates; |
| 317 | private ImmutableList<Map.Entry<K, Binding<V>>> mapBindings; |
limpbizkit | c75363a | 2008-05-13 07:16:26 +0000 | [diff] [blame] | 318 | |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 319 | private RealMapBinder(Binder binder, TypeLiteral<K> keyType, TypeLiteral<V> valueType, |
flan | f618960 | 2014-11-04 07:32:47 -0800 | [diff] [blame] | 320 | Key<Map<K, V>> mapKey, Multibinder<Map.Entry<K, Provider<V>>> entrySetBinder) { |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 321 | this.keyType = keyType; |
limpbizkit | e97e15b | 2008-05-16 14:53:49 +0000 | [diff] [blame] | 322 | this.valueType = valueType; |
limpbizkit | c75363a | 2008-05-13 07:16:26 +0000 | [diff] [blame] | 323 | this.mapKey = mapKey; |
flan | f618960 | 2014-11-04 07:32:47 -0800 | [diff] [blame] | 324 | this.providerMapKey = mapKey.ofType(mapOfProviderOf(keyType, valueType)); |
| 325 | this.javaxProviderMapKey = mapKey.ofType(mapOfJavaxProviderOf(keyType, valueType)); |
| 326 | this.multimapKey = mapKey.ofType(mapOf(keyType, setOf(valueType))); |
| 327 | this.providerMultimapKey = mapKey.ofType(mapOfSetOfProviderOf(keyType, valueType)); |
limpbizkit | e97e15b | 2008-05-16 14:53:49 +0000 | [diff] [blame] | 328 | this.entrySetBinder = (RealMultibinder<Entry<K, Provider<V>>>) entrySetBinder; |
| 329 | this.binder = binder; |
Christian Edward Gruber | cade897 | 2014-04-01 15:07:02 -0700 | [diff] [blame] | 330 | this.duplicateKeyErrorMessages = Maps.newHashMap(); |
| 331 | } |
| 332 | |
| 333 | /** Sets the error message to be shown if the key had duplicate non-equal bindings. */ |
| 334 | void updateDuplicateKeyMessage(K k, String errMsg) { |
| 335 | duplicateKeyErrorMessages.put(k, errMsg); |
limpbizkit | c75363a | 2008-05-13 07:16:26 +0000 | [diff] [blame] | 336 | } |
| 337 | |
netdpb | 0efcc6c | 2009-07-07 20:13:32 +0000 | [diff] [blame] | 338 | @Override |
limpbizkit | 398017a | 2009-06-23 06:30:37 +0000 | [diff] [blame] | 339 | public MapBinder<K, V> permitDuplicates() { |
| 340 | entrySetBinder.permitDuplicates(); |
netdpb | fb3a896 | 2009-07-08 19:49:54 +0000 | [diff] [blame] | 341 | binder.install(new MultimapBinder<K, V>( |
| 342 | multimapKey, providerMultimapKey, entrySetBinder.getSetKey())); |
limpbizkit | 398017a | 2009-06-23 06:30:37 +0000 | [diff] [blame] | 343 | return this; |
| 344 | } |
| 345 | |
limpbizkit | e97e15b | 2008-05-16 14:53:49 +0000 | [diff] [blame] | 346 | /** |
| 347 | * This creates two bindings. One for the {@code Map.Entry<K, Provider<V>>} |
| 348 | * and another for {@code V}. |
| 349 | */ |
| 350 | @Override public LinkedBindingBuilder<V> addBinding(K key) { |
kevinb9n | 1601ae5 | 2008-06-03 22:21:04 +0000 | [diff] [blame] | 351 | checkNotNull(key, "key"); |
limpbizkit | 72d11dd | 2008-11-02 07:59:13 +0000 | [diff] [blame] | 352 | checkConfiguration(!isInitialized(), "MapBinder was already initialized"); |
limpbizkit | e97e15b | 2008-05-16 14:53:49 +0000 | [diff] [blame] | 353 | |
Sam Berlin | c34e018 | 2014-08-06 11:56:26 -0400 | [diff] [blame] | 354 | Key<V> valueKey = Key.get(valueType, |
| 355 | new RealElement(entrySetBinder.getSetName(), MAPBINDER, keyType.toString())); |
Christian Edward Gruber | cade897 | 2014-04-01 15:07:02 -0700 | [diff] [blame] | 356 | entrySetBinder.addBinding().toProvider(new ProviderMapEntry<K, V>( |
Christian Edward Gruber | 6c69bcf | 2013-09-18 10:55:21 -0700 | [diff] [blame] | 357 | key, binder.getProvider(valueKey), valueKey)); |
Sam Berlin | c34e018 | 2014-08-06 11:56:26 -0400 | [diff] [blame] | 358 | return binder.bind(valueKey); |
limpbizkit | c75363a | 2008-05-13 07:16:26 +0000 | [diff] [blame] | 359 | } |
| 360 | |
Sam Berlin | c34e018 | 2014-08-06 11:56:26 -0400 | [diff] [blame] | 361 | @Override public void configure(Binder binder) { |
limpbizkit | 72d11dd | 2008-11-02 07:59:13 +0000 | [diff] [blame] | 362 | checkConfiguration(!isInitialized(), "MapBinder was already initialized"); |
limpbizkit | e97e15b | 2008-05-16 14:53:49 +0000 | [diff] [blame] | 363 | |
Sam Berlin | ca0cc18 | 2014-08-06 11:57:22 -0400 | [diff] [blame] | 364 | ImmutableSet<Dependency<?>> dependencies |
limpbizkit | ddb3862 | 2008-12-29 05:21:16 +0000 | [diff] [blame] | 365 | = ImmutableSet.<Dependency<?>>of(Dependency.get(entrySetBinder.getSetKey())); |
| 366 | |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 367 | // Binds a Map<K, Provider<V>> from a collection of Set<Entry<K, Provider<V>>. |
Sam Berlin | ca0cc18 | 2014-08-06 11:57:22 -0400 | [diff] [blame] | 368 | Provider<Set<Entry<K, Provider<V>>>> entrySetProvider = binder |
limpbizkit | 9532e62 | 2008-06-18 08:20:54 +0000 | [diff] [blame] | 369 | .getProvider(entrySetBinder.getSetKey()); |
Sam Berlin | ca0cc18 | 2014-08-06 11:57:22 -0400 | [diff] [blame] | 370 | |
Sam Berlin | 3338a48 | 2013-12-06 17:07:34 -0500 | [diff] [blame] | 371 | binder.bind(providerMapKey).toProvider( |
Sam Berlin | ca0cc18 | 2014-08-06 11:57:22 -0400 | [diff] [blame] | 372 | new RealProviderMapProvider(dependencies, entrySetProvider)); |
limpbizkit | 9532e62 | 2008-06-18 08:20:54 +0000 | [diff] [blame] | 373 | |
Sam Berlin | 3338a48 | 2013-12-06 17:07:34 -0500 | [diff] [blame] | 374 | // The map this exposes is internally an ImmutableMap, so it's OK to massage |
| 375 | // the guice Provider to javax Provider in the value (since Guice provider |
| 376 | // implements javax Provider). |
| 377 | @SuppressWarnings("unchecked") |
| 378 | Key massagedProviderMapKey = (Key)providerMapKey; |
| 379 | binder.bind(javaxProviderMapKey).to(massagedProviderMapKey); |
limpbizkit | c75363a | 2008-05-13 07:16:26 +0000 | [diff] [blame] | 380 | |
Sam Berlin | ca0cc18 | 2014-08-06 11:57:22 -0400 | [diff] [blame] | 381 | Provider<Map<K, Provider<V>>> mapProvider = binder.getProvider(providerMapKey); |
| 382 | binder.bind(mapKey).toProvider(new RealMapProvider(dependencies, mapProvider)); |
limpbizkit | c75363a | 2008-05-13 07:16:26 +0000 | [diff] [blame] | 383 | } |
| 384 | |
Sam Berlin | c34e018 | 2014-08-06 11:56:26 -0400 | [diff] [blame] | 385 | boolean containsElement(Element element) { |
Christian Edward Gruber | cade897 | 2014-04-01 15:07:02 -0700 | [diff] [blame] | 386 | if (entrySetBinder.containsElement(element)) { |
| 387 | return true; |
| 388 | } else { |
| 389 | Key<?> key; |
| 390 | if (element instanceof Binding) { |
| 391 | key = ((Binding<?>)element).getKey(); |
| 392 | } else if (element instanceof ProviderLookup) { |
| 393 | key = ((ProviderLookup<?>)element).getKey(); |
| 394 | } else { |
| 395 | return false; // cannot match; |
| 396 | } |
| 397 | |
| 398 | return key.equals(mapKey) |
| 399 | || key.equals(providerMapKey) |
| 400 | || key.equals(javaxProviderMapKey) |
| 401 | || key.equals(multimapKey) |
| 402 | || key.equals(providerMultimapKey) |
| 403 | || key.equals(entrySetBinder.getSetKey()) |
| 404 | || matchesValueKey(key); |
| 405 | } |
| 406 | } |
| 407 | |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 408 | /** Returns true if the key indicates this is a value in the map. */ |
Christian Edward Gruber | 6c69bcf | 2013-09-18 10:55:21 -0700 | [diff] [blame] | 409 | private boolean matchesValueKey(Key<?> key) { |
Christian Edward Gruber | cade897 | 2014-04-01 15:07:02 -0700 | [diff] [blame] | 410 | return key.getAnnotation() instanceof RealElement |
| 411 | && ((RealElement) key.getAnnotation()).setName().equals(entrySetBinder.getSetName()) |
| 412 | && ((RealElement) key.getAnnotation()).type() == MAPBINDER |
Sam Berlin | c34e018 | 2014-08-06 11:56:26 -0400 | [diff] [blame] | 413 | && ((RealElement) key.getAnnotation()).keyType().equals(keyType.toString()) |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 414 | && key.getTypeLiteral().equals(valueType); |
| 415 | } |
| 416 | |
limpbizkit | e97e15b | 2008-05-16 14:53:49 +0000 | [diff] [blame] | 417 | private boolean isInitialized() { |
| 418 | return binder == null; |
| 419 | } |
| 420 | |
limpbizkit | c75363a | 2008-05-13 07:16:26 +0000 | [diff] [blame] | 421 | @Override public boolean equals(Object o) { |
| 422 | return o instanceof RealMapBinder |
limpbizkit | e97e15b | 2008-05-16 14:53:49 +0000 | [diff] [blame] | 423 | && ((RealMapBinder<?, ?>) o).mapKey.equals(mapKey); |
limpbizkit | c75363a | 2008-05-13 07:16:26 +0000 | [diff] [blame] | 424 | } |
| 425 | |
| 426 | @Override public int hashCode() { |
| 427 | return mapKey.hashCode(); |
| 428 | } |
limpbizkit | e97e15b | 2008-05-16 14:53:49 +0000 | [diff] [blame] | 429 | |
Sam Berlin | ca0cc18 | 2014-08-06 11:57:22 -0400 | [diff] [blame] | 430 | final class RealProviderMapProvider |
| 431 | extends RealMapBinderProviderWithDependencies<Map<K, Provider<V>>> { |
| 432 | private final ImmutableSet<Dependency<?>> dependencies; |
| 433 | private final Provider<Set<Entry<K, Provider<V>>>> entrySetProvider; |
| 434 | private Map<K, Provider<V>> providerMap; |
| 435 | |
| 436 | private RealProviderMapProvider( |
| 437 | ImmutableSet<Dependency<?>> dependencies, |
| 438 | Provider<Set<Entry<K, Provider<V>>>> entrySetProvider) { |
| 439 | super(mapKey); |
| 440 | this.dependencies = dependencies; |
| 441 | this.entrySetProvider = entrySetProvider; |
| 442 | } |
| 443 | |
| 444 | @Toolable @Inject void initialize(Injector injector) { |
| 445 | RealMapBinder.this.binder = null; |
| 446 | permitDuplicates = entrySetBinder.permitsDuplicates(injector); |
| 447 | |
| 448 | Map<K, Provider<V>> providerMapMutable = new LinkedHashMap<K, Provider<V>>(); |
| 449 | List<Map.Entry<K, Binding<V>>> bindingsMutable = Lists.newArrayList(); |
| 450 | Indexer indexer = new Indexer(injector); |
| 451 | Multimap<K, IndexedBinding> index = HashMultimap.create(); |
| 452 | Set<K> duplicateKeys = null; |
| 453 | for (Entry<K, Provider<V>> entry : entrySetProvider.get()) { |
| 454 | ProviderMapEntry<K, V> providerEntry = (ProviderMapEntry<K, V>) entry; |
| 455 | Key<V> valueKey = providerEntry.getValueKey(); |
| 456 | Binding<V> valueBinding = injector.getBinding(valueKey); |
| 457 | // If this isn't a dup due to an exact same binding, add it. |
sameb | 1bf1e9e | 2014-12-18 14:44:16 -0800 | [diff] [blame^] | 458 | if (index.put(providerEntry.getKey(), valueBinding.acceptTargetVisitor(indexer))) { |
| 459 | Provider<V> previous = providerMapMutable.put(providerEntry.getKey(), |
| 460 | new ValueProvider<V>(providerEntry.getValue(), valueBinding)); |
Sam Berlin | ca0cc18 | 2014-08-06 11:57:22 -0400 | [diff] [blame] | 461 | if (previous != null && !permitDuplicates) { |
| 462 | if (duplicateKeys == null) { |
| 463 | duplicateKeys = Sets.newHashSet(); |
| 464 | } |
sameb | 1bf1e9e | 2014-12-18 14:44:16 -0800 | [diff] [blame^] | 465 | duplicateKeys.add(providerEntry.getKey()); |
Sam Berlin | ca0cc18 | 2014-08-06 11:57:22 -0400 | [diff] [blame] | 466 | } |
sameb | 1bf1e9e | 2014-12-18 14:44:16 -0800 | [diff] [blame^] | 467 | bindingsMutable.add(Maps.immutableEntry(providerEntry.getKey(), valueBinding)); |
Sam Berlin | ca0cc18 | 2014-08-06 11:57:22 -0400 | [diff] [blame] | 468 | } |
| 469 | } |
| 470 | if (duplicateKeys != null) { |
| 471 | // Must use a ListMultimap in case more than one binding has the same source |
| 472 | // and is listed multiple times. |
| 473 | Multimap<K, String> dups = newLinkedKeyArrayValueMultimap(); |
| 474 | for (Map.Entry<K, Binding<V>> entry : bindingsMutable) { |
| 475 | if (duplicateKeys.contains(entry.getKey())) { |
| 476 | dups.put(entry.getKey(), "\t at " + Errors.convert(entry.getValue().getSource())); |
| 477 | } |
| 478 | } |
| 479 | StringBuilder sb = new StringBuilder("Map injection failed due to duplicated key "); |
| 480 | boolean first = true; |
| 481 | for (K key : dups.keySet()) { |
| 482 | if (first) { |
| 483 | first = false; |
| 484 | if (duplicateKeyErrorMessages.containsKey(key)) { |
| 485 | sb.setLength(0); |
| 486 | sb.append(duplicateKeyErrorMessages.get(key)); |
| 487 | } else { |
| 488 | sb.append("\"" + key + "\", from bindings:\n"); |
| 489 | } |
| 490 | } else { |
| 491 | if (duplicateKeyErrorMessages.containsKey(key)) { |
| 492 | sb.append("\n and " + duplicateKeyErrorMessages.get(key)); |
| 493 | } else { |
| 494 | sb.append("\n and key: \"" + key + "\", from bindings:\n"); |
| 495 | } |
| 496 | } |
| 497 | Joiner.on('\n').appendTo(sb, dups.get(key)).append("\n"); |
| 498 | } |
| 499 | checkConfiguration(false, sb.toString()); |
| 500 | } |
| 501 | |
| 502 | providerMap = ImmutableMap.copyOf(providerMapMutable); |
| 503 | mapBindings = ImmutableList.copyOf(bindingsMutable); |
| 504 | } |
| 505 | |
| 506 | @Override public Map<K, Provider<V>> get() { |
| 507 | return providerMap; |
| 508 | } |
| 509 | |
| 510 | @Override public Set<Dependency<?>> getDependencies() { |
| 511 | return dependencies; |
| 512 | } |
| 513 | } |
| 514 | |
| 515 | final class RealMapProvider extends RealMapWithExtensionProvider<Map<K, V>> { |
| 516 | private final ImmutableSet<Dependency<?>> dependencies; |
| 517 | private final Provider<Map<K, Provider<V>>> mapProvider; |
| 518 | |
| 519 | private RealMapProvider( |
| 520 | ImmutableSet<Dependency<?>> dependencies, |
| 521 | Provider<Map<K, Provider<V>>> mapProvider) { |
| 522 | super(mapKey); |
| 523 | this.dependencies = dependencies; |
| 524 | this.mapProvider = mapProvider; |
| 525 | } |
| 526 | |
| 527 | @Override public Map<K, V> get() { |
lukes | b159fc5 | 2014-09-12 11:38:41 -0700 | [diff] [blame] | 528 | // We can initialize the internal table efficiently this way and then swap the values |
| 529 | // one by one. |
| 530 | Map<K, Object> map = new LinkedHashMap<K, Object>(mapProvider.get()); |
| 531 | for (Entry<K, Object> entry : map.entrySet()) { |
| 532 | @SuppressWarnings("unchecked") // we initialized the entries with providers |
sameb | 1bf1e9e | 2014-12-18 14:44:16 -0800 | [diff] [blame^] | 533 | ValueProvider<V> provider = (ValueProvider<V>)entry.getValue(); |
| 534 | V value = provider.get(); |
Sam Berlin | ca0cc18 | 2014-08-06 11:57:22 -0400 | [diff] [blame] | 535 | checkConfiguration(value != null, |
sameb | 1bf1e9e | 2014-12-18 14:44:16 -0800 | [diff] [blame^] | 536 | "Map injection failed due to null value for key \"%s\", bound at: %s", |
| 537 | entry.getKey(), |
| 538 | provider.getValueBinding().getSource()); |
lukes | b159fc5 | 2014-09-12 11:38:41 -0700 | [diff] [blame] | 539 | entry.setValue(value); |
Sam Berlin | ca0cc18 | 2014-08-06 11:57:22 -0400 | [diff] [blame] | 540 | } |
lukes | b159fc5 | 2014-09-12 11:38:41 -0700 | [diff] [blame] | 541 | @SuppressWarnings("unchecked") // if we exited the loop then we replaced all Providers |
| 542 | Map<K, V> typedMap = (Map<K, V>) map; |
| 543 | return Collections.unmodifiableMap(typedMap); |
Sam Berlin | ca0cc18 | 2014-08-06 11:57:22 -0400 | [diff] [blame] | 544 | } |
| 545 | |
| 546 | @Override public Set<Dependency<?>> getDependencies() { |
| 547 | return dependencies; |
| 548 | } |
| 549 | |
| 550 | @SuppressWarnings("unchecked") |
| 551 | @Override |
| 552 | public <B, R> R acceptExtensionVisitor(BindingTargetVisitor<B, R> visitor, |
| 553 | ProviderInstanceBinding<? extends B> binding) { |
| 554 | if (visitor instanceof MultibindingsTargetVisitor) { |
| 555 | return ((MultibindingsTargetVisitor<Map<K, V>, R>)visitor).visit(this); |
| 556 | } else { |
| 557 | return visitor.visit(binding); |
| 558 | } |
| 559 | } |
| 560 | |
| 561 | @Override public Key<Map<K, V>> getMapKey() { |
| 562 | return mapKey; |
| 563 | } |
| 564 | |
| 565 | @Override public TypeLiteral<?> getKeyTypeLiteral() { |
| 566 | return keyType; |
| 567 | } |
| 568 | |
| 569 | @Override public TypeLiteral<?> getValueTypeLiteral() { |
| 570 | return valueType; |
| 571 | } |
| 572 | |
| 573 | @SuppressWarnings("unchecked") |
| 574 | @Override |
| 575 | public List<Entry<?, Binding<?>>> getEntries() { |
| 576 | if (isInitialized()) { |
| 577 | return (List)mapBindings; // safe because mapBindings is immutable |
| 578 | } else { |
| 579 | throw new UnsupportedOperationException( |
| 580 | "getElements() not supported for module bindings"); |
| 581 | } |
| 582 | } |
| 583 | |
| 584 | @Override public boolean permitsDuplicates() { |
| 585 | if (isInitialized()) { |
| 586 | return permitDuplicates; |
| 587 | } else { |
| 588 | throw new UnsupportedOperationException( |
| 589 | "permitsDuplicates() not supported for module bindings"); |
| 590 | } |
| 591 | } |
| 592 | |
| 593 | @Override public boolean containsElement(Element element) { |
| 594 | return RealMapBinder.this.containsElement(element); |
| 595 | } |
| 596 | } |
| 597 | |
netdpb | fb3a896 | 2009-07-08 19:49:54 +0000 | [diff] [blame] | 598 | /** |
| 599 | * Binds {@code Map<K, Set<V>>} and {{@code Map<K, Set<Provider<V>>>}. |
| 600 | */ |
Sam Berlin | 6dec441 | 2014-08-11 13:23:04 -0400 | [diff] [blame] | 601 | static final class MultimapBinder<K, V> implements Module { |
netdpb | fb3a896 | 2009-07-08 19:49:54 +0000 | [diff] [blame] | 602 | |
| 603 | private final Key<Map<K, Set<V>>> multimapKey; |
| 604 | private final Key<Map<K, Set<Provider<V>>>> providerMultimapKey; |
| 605 | private final Key<Set<Entry<K,Provider<V>>>> entrySetKey; |
| 606 | |
| 607 | public MultimapBinder( |
| 608 | Key<Map<K, Set<V>>> multimapKey, |
| 609 | Key<Map<K, Set<Provider<V>>>> providerMultimapKey, |
| 610 | Key<Set<Entry<K,Provider<V>>>> entrySetKey) { |
| 611 | this.multimapKey = multimapKey; |
| 612 | this.providerMultimapKey = providerMultimapKey; |
| 613 | this.entrySetKey = entrySetKey; |
| 614 | } |
| 615 | |
Sam Berlin | c34e018 | 2014-08-06 11:56:26 -0400 | [diff] [blame] | 616 | @Override public void configure(Binder binder) { |
Sam Berlin | ca0cc18 | 2014-08-06 11:57:22 -0400 | [diff] [blame] | 617 | ImmutableSet<Dependency<?>> dependencies |
netdpb | fb3a896 | 2009-07-08 19:49:54 +0000 | [diff] [blame] | 618 | = ImmutableSet.<Dependency<?>>of(Dependency.get(entrySetKey)); |
| 619 | |
Sam Berlin | ca0cc18 | 2014-08-06 11:57:22 -0400 | [diff] [blame] | 620 | Provider<Set<Entry<K, Provider<V>>>> entrySetProvider = |
netdpb | fb3a896 | 2009-07-08 19:49:54 +0000 | [diff] [blame] | 621 | binder.getProvider(entrySetKey); |
| 622 | // Binds a Map<K, Set<Provider<V>>> from a collection of Map<Entry<K, Provider<V>> if |
| 623 | // permitDuplicates was called. |
| 624 | binder.bind(providerMultimapKey).toProvider( |
Sam Berlin | ca0cc18 | 2014-08-06 11:57:22 -0400 | [diff] [blame] | 625 | new RealProviderMultimapProvider(dependencies, entrySetProvider)); |
netdpb | fb3a896 | 2009-07-08 19:49:54 +0000 | [diff] [blame] | 626 | |
Sam Berlin | ca0cc18 | 2014-08-06 11:57:22 -0400 | [diff] [blame] | 627 | Provider<Map<K, Set<Provider<V>>>> multimapProvider = |
netdpb | fb3a896 | 2009-07-08 19:49:54 +0000 | [diff] [blame] | 628 | binder.getProvider(providerMultimapKey); |
Sam Berlin | ca0cc18 | 2014-08-06 11:57:22 -0400 | [diff] [blame] | 629 | binder.bind(multimapKey).toProvider( |
| 630 | new RealMultimapProvider(dependencies, multimapProvider)); |
netdpb | fb3a896 | 2009-07-08 19:49:54 +0000 | [diff] [blame] | 631 | } |
drew.mclaughlin | 895fa39 | 2011-06-16 20:00:26 +0000 | [diff] [blame] | 632 | |
| 633 | @Override public int hashCode() { |
| 634 | return multimapKey.hashCode(); |
| 635 | } |
| 636 | |
| 637 | @Override public boolean equals(Object o) { |
Christian Edward Gruber | 96e81ba | 2013-09-18 10:55:21 -0700 | [diff] [blame] | 638 | return o instanceof MultimapBinder |
drew.mclaughlin | 895fa39 | 2011-06-16 20:00:26 +0000 | [diff] [blame] | 639 | && ((MultimapBinder<?, ?>) o).multimapKey.equals(multimapKey); |
| 640 | } |
Sam Berlin | ca0cc18 | 2014-08-06 11:57:22 -0400 | [diff] [blame] | 641 | |
| 642 | final class RealProviderMultimapProvider |
| 643 | extends RealMapBinderProviderWithDependencies<Map<K, Set<Provider<V>>>> { |
| 644 | private final ImmutableSet<Dependency<?>> dependencies; |
| 645 | private final Provider<Set<Entry<K, Provider<V>>>> entrySetProvider; |
| 646 | private Map<K, Set<Provider<V>>> providerMultimap; |
| 647 | |
| 648 | private RealProviderMultimapProvider(ImmutableSet<Dependency<?>> dependencies, |
| 649 | Provider<Set<Entry<K, Provider<V>>>> entrySetProvider) { |
| 650 | super(multimapKey); |
| 651 | this.dependencies = dependencies; |
| 652 | this.entrySetProvider = entrySetProvider; |
| 653 | } |
| 654 | |
| 655 | @SuppressWarnings("unused") |
| 656 | @Inject void initialize(Injector injector) { |
| 657 | Map<K, ImmutableSet.Builder<Provider<V>>> providerMultimapMutable = |
| 658 | new LinkedHashMap<K, ImmutableSet.Builder<Provider<V>>>(); |
| 659 | for (Entry<K, Provider<V>> entry : entrySetProvider.get()) { |
| 660 | if (!providerMultimapMutable.containsKey(entry.getKey())) { |
| 661 | providerMultimapMutable.put( |
| 662 | entry.getKey(), ImmutableSet.<Provider<V>>builder()); |
| 663 | } |
| 664 | providerMultimapMutable.get(entry.getKey()).add(entry.getValue()); |
| 665 | } |
| 666 | |
| 667 | ImmutableMap.Builder<K, Set<Provider<V>>> providerMultimapBuilder = |
| 668 | ImmutableMap.builder(); |
| 669 | for (Entry<K, ImmutableSet.Builder<Provider<V>>> entry |
| 670 | : providerMultimapMutable.entrySet()) { |
| 671 | providerMultimapBuilder.put(entry.getKey(), entry.getValue().build()); |
| 672 | } |
| 673 | providerMultimap = providerMultimapBuilder.build(); |
| 674 | } |
| 675 | |
| 676 | @Override public Map<K, Set<Provider<V>>> get() { |
| 677 | return providerMultimap; |
| 678 | } |
| 679 | |
| 680 | @Override public Set<Dependency<?>> getDependencies() { |
| 681 | return dependencies; |
| 682 | } |
| 683 | } |
| 684 | |
| 685 | final class RealMultimapProvider |
| 686 | extends RealMapBinderProviderWithDependencies<Map<K, Set<V>>> { |
| 687 | private final ImmutableSet<Dependency<?>> dependencies; |
| 688 | private final Provider<Map<K, Set<Provider<V>>>> multimapProvider; |
| 689 | |
| 690 | RealMultimapProvider( |
| 691 | ImmutableSet<Dependency<?>> dependencies, |
| 692 | Provider<Map<K, Set<Provider<V>>>> multimapProvider) { |
| 693 | super(multimapKey); |
| 694 | this.dependencies = dependencies; |
| 695 | this.multimapProvider = multimapProvider; |
| 696 | } |
| 697 | |
| 698 | @Override public Map<K, Set<V>> get() { |
| 699 | ImmutableMap.Builder<K, Set<V>> multimapBuilder = ImmutableMap.builder(); |
| 700 | for (Entry<K, Set<Provider<V>>> entry : multimapProvider.get().entrySet()) { |
| 701 | K key = entry.getKey(); |
| 702 | ImmutableSet.Builder<V> valuesBuilder = ImmutableSet.builder(); |
| 703 | for (Provider<V> valueProvider : entry.getValue()) { |
| 704 | V value = valueProvider.get(); |
| 705 | checkConfiguration(value != null, |
| 706 | "Multimap injection failed due to null value for key \"%s\"", key); |
| 707 | valuesBuilder.add(value); |
| 708 | } |
| 709 | multimapBuilder.put(key, valuesBuilder.build()); |
| 710 | } |
| 711 | return multimapBuilder.build(); |
| 712 | } |
| 713 | |
| 714 | @Override public Set<Dependency<?>> getDependencies() { |
| 715 | return dependencies; |
| 716 | } |
| 717 | } |
netdpb | fb3a896 | 2009-07-08 19:49:54 +0000 | [diff] [blame] | 718 | } |
| 719 | |
sameb | 1bf1e9e | 2014-12-18 14:44:16 -0800 | [diff] [blame^] | 720 | static final class ValueProvider<V> implements Provider<V> { |
| 721 | private final Provider<V> delegate; |
| 722 | private final Binding<V> binding; |
| 723 | |
| 724 | ValueProvider(Provider<V> delegate, Binding<V> binding) { |
| 725 | this.delegate = delegate; |
| 726 | this.binding = binding; |
| 727 | } |
| 728 | |
| 729 | @Override public V get() { |
| 730 | return delegate.get(); |
| 731 | } |
| 732 | |
| 733 | public Binding<V> getValueBinding() { |
| 734 | return binding; |
| 735 | } |
| 736 | } |
| 737 | |
Christian Edward Gruber | 6c69bcf | 2013-09-18 10:55:21 -0700 | [diff] [blame] | 738 | /** |
Sam Berlin | c34e018 | 2014-08-06 11:56:26 -0400 | [diff] [blame] | 739 | * A Provider that Map.Entry that is also a Provider. The key is the entry in the |
| 740 | * map this corresponds to and the value is the provider of the user's binding. |
| 741 | * This returns itself as the Provider.get value. |
Christian Edward Gruber | 6c69bcf | 2013-09-18 10:55:21 -0700 | [diff] [blame] | 742 | */ |
Sam Berlin | c34e018 | 2014-08-06 11:56:26 -0400 | [diff] [blame] | 743 | static final class ProviderMapEntry<K, V> implements |
Christian Edward Gruber | cade897 | 2014-04-01 15:07:02 -0700 | [diff] [blame] | 744 | ProviderWithDependencies<Map.Entry<K, Provider<V>>>, Map.Entry<K, Provider<V>> { |
limpbizkit | e97e15b | 2008-05-16 14:53:49 +0000 | [diff] [blame] | 745 | private final K key; |
Christian Edward Gruber | 6c69bcf | 2013-09-18 10:55:21 -0700 | [diff] [blame] | 746 | private final Provider<V> provider; |
Sam Berlin | c34e018 | 2014-08-06 11:56:26 -0400 | [diff] [blame] | 747 | private final Key<V> valueKey; |
limpbizkit | e97e15b | 2008-05-16 14:53:49 +0000 | [diff] [blame] | 748 | |
Christian Edward Gruber | 6c69bcf | 2013-09-18 10:55:21 -0700 | [diff] [blame] | 749 | private ProviderMapEntry(K key, Provider<V> provider, Key<V> valueKey) { |
limpbizkit | e97e15b | 2008-05-16 14:53:49 +0000 | [diff] [blame] | 750 | this.key = key; |
Christian Edward Gruber | 6c69bcf | 2013-09-18 10:55:21 -0700 | [diff] [blame] | 751 | this.provider = provider; |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 752 | this.valueKey = valueKey; |
| 753 | } |
Sam Berlin | c34e018 | 2014-08-06 11:56:26 -0400 | [diff] [blame] | 754 | |
| 755 | @Override public Entry<K, Provider<V>> get() { |
Christian Edward Gruber | cade897 | 2014-04-01 15:07:02 -0700 | [diff] [blame] | 756 | return this; |
| 757 | } |
Sam Berlin | c34e018 | 2014-08-06 11:56:26 -0400 | [diff] [blame] | 758 | |
| 759 | @Override public Set<Dependency<?>> getDependencies() { |
Christian Edward Gruber | cade897 | 2014-04-01 15:07:02 -0700 | [diff] [blame] | 760 | return ((HasDependencies) provider).getDependencies(); |
| 761 | } |
Christian Edward Gruber | 96e81ba | 2013-09-18 10:55:21 -0700 | [diff] [blame] | 762 | |
Christian Edward Gruber | 6c69bcf | 2013-09-18 10:55:21 -0700 | [diff] [blame] | 763 | public Key<V> getValueKey() { |
Sam Berlin | c34e018 | 2014-08-06 11:56:26 -0400 | [diff] [blame] | 764 | return valueKey; |
limpbizkit | e97e15b | 2008-05-16 14:53:49 +0000 | [diff] [blame] | 765 | } |
| 766 | |
Sam Berlin | c34e018 | 2014-08-06 11:56:26 -0400 | [diff] [blame] | 767 | @Override public K getKey() { |
limpbizkit | e97e15b | 2008-05-16 14:53:49 +0000 | [diff] [blame] | 768 | return key; |
| 769 | } |
| 770 | |
Sam Berlin | c34e018 | 2014-08-06 11:56:26 -0400 | [diff] [blame] | 771 | @Override public Provider<V> getValue() { |
Christian Edward Gruber | 6c69bcf | 2013-09-18 10:55:21 -0700 | [diff] [blame] | 772 | return provider; |
limpbizkit | e97e15b | 2008-05-16 14:53:49 +0000 | [diff] [blame] | 773 | } |
| 774 | |
Sam Berlin | c34e018 | 2014-08-06 11:56:26 -0400 | [diff] [blame] | 775 | @Override public Provider<V> setValue(Provider<V> value) { |
limpbizkit | e97e15b | 2008-05-16 14:53:49 +0000 | [diff] [blame] | 776 | throw new UnsupportedOperationException(); |
| 777 | } |
| 778 | |
Sam Berlin | c34e018 | 2014-08-06 11:56:26 -0400 | [diff] [blame] | 779 | @Override public boolean equals(Object obj) { |
| 780 | if (obj instanceof Map.Entry) { |
| 781 | Map.Entry o = (Map.Entry)obj; |
| 782 | return Objects.equal(key, o.getKey()) |
| 783 | && Objects.equal(provider, o.getValue()); |
| 784 | } |
| 785 | return false; |
limpbizkit | e97e15b | 2008-05-16 14:53:49 +0000 | [diff] [blame] | 786 | } |
| 787 | |
| 788 | @Override public int hashCode() { |
Sam Berlin | c34e018 | 2014-08-06 11:56:26 -0400 | [diff] [blame] | 789 | return key.hashCode() ^ provider.hashCode(); |
limpbizkit | e97e15b | 2008-05-16 14:53:49 +0000 | [diff] [blame] | 790 | } |
| 791 | |
| 792 | @Override public String toString() { |
Sam Berlin | c34e018 | 2014-08-06 11:56:26 -0400 | [diff] [blame] | 793 | return "ProviderMapEntry(" + key + ", " + provider + ")"; |
limpbizkit | e97e15b | 2008-05-16 14:53:49 +0000 | [diff] [blame] | 794 | } |
| 795 | } |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 796 | |
| 797 | private static abstract class RealMapWithExtensionProvider<T> |
| 798 | extends RealMapBinderProviderWithDependencies<T> |
| 799 | implements ProviderWithExtensionVisitor<T>, MapBinderBinding<T> { |
| 800 | public RealMapWithExtensionProvider(Object equality) { |
| 801 | super(equality); |
| 802 | } |
| 803 | } |
Christian Edward Gruber | 96e81ba | 2013-09-18 10:55:21 -0700 | [diff] [blame] | 804 | |
sberlin@gmail.com | 7bcec88 | 2010-06-24 01:03:26 +0000 | [diff] [blame] | 805 | /** |
| 806 | * A base class for ProviderWithDependencies that need equality |
| 807 | * based on a specific object. |
| 808 | */ |
| 809 | private static abstract class RealMapBinderProviderWithDependencies<T> implements ProviderWithDependencies<T> { |
| 810 | private final Object equality; |
Christian Edward Gruber | 96e81ba | 2013-09-18 10:55:21 -0700 | [diff] [blame] | 811 | |
sberlin@gmail.com | 7bcec88 | 2010-06-24 01:03:26 +0000 | [diff] [blame] | 812 | public RealMapBinderProviderWithDependencies(Object equality) { |
| 813 | this.equality = equality; |
| 814 | } |
Christian Edward Gruber | 96e81ba | 2013-09-18 10:55:21 -0700 | [diff] [blame] | 815 | |
sberlin@gmail.com | 7bcec88 | 2010-06-24 01:03:26 +0000 | [diff] [blame] | 816 | @Override |
| 817 | public boolean equals(Object obj) { |
| 818 | return this.getClass() == obj.getClass() && |
| 819 | equality.equals(((RealMapBinderProviderWithDependencies<?>)obj).equality); |
| 820 | } |
Christian Edward Gruber | 96e81ba | 2013-09-18 10:55:21 -0700 | [diff] [blame] | 821 | |
Christian Edward Gruber | 6c69bcf | 2013-09-18 10:55:21 -0700 | [diff] [blame] | 822 | @Override |
| 823 | public int hashCode() { |
| 824 | return equality.hashCode(); |
| 825 | } |
sberlin@gmail.com | 7bcec88 | 2010-06-24 01:03:26 +0000 | [diff] [blame] | 826 | } |
Christian Edward Gruber | d1d7ef3 | 2014-04-01 15:10:51 -0700 | [diff] [blame] | 827 | |
| 828 | private Multimap<K, String> newLinkedKeyArrayValueMultimap() { |
| 829 | return Multimaps.newListMultimap( |
| 830 | new LinkedHashMap<K, Collection<String>>(), |
| 831 | new Supplier<List<String>>() { |
| 832 | @Override public List<String> get() { |
| 833 | return Lists.newArrayList(); |
| 834 | } |
| 835 | }); |
| 836 | } |
limpbizkit | c75363a | 2008-05-13 07:16:26 +0000 | [diff] [blame] | 837 | } |
| 838 | } |