14e11457f34addf5d10fe0c31cefd54c75c37b540sberlin/**
24e11457f34addf5d10fe0c31cefd54c75c37b540sberlin * Copyright (C) 2010 Google Inc.
34e11457f34addf5d10fe0c31cefd54c75c37b540sberlin *
44e11457f34addf5d10fe0c31cefd54c75c37b540sberlin * Licensed under the Apache License, Version 2.0 (the "License");
54e11457f34addf5d10fe0c31cefd54c75c37b540sberlin * you may not use this file except in compliance with the License.
64e11457f34addf5d10fe0c31cefd54c75c37b540sberlin * You may obtain a copy of the License at
74e11457f34addf5d10fe0c31cefd54c75c37b540sberlin *
84e11457f34addf5d10fe0c31cefd54c75c37b540sberlin * http://www.apache.org/licenses/LICENSE-2.0
94e11457f34addf5d10fe0c31cefd54c75c37b540sberlin *
104e11457f34addf5d10fe0c31cefd54c75c37b540sberlin * Unless required by applicable law or agreed to in writing, software
114e11457f34addf5d10fe0c31cefd54c75c37b540sberlin * distributed under the License is distributed on an "AS IS" BASIS,
124e11457f34addf5d10fe0c31cefd54c75c37b540sberlin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
134e11457f34addf5d10fe0c31cefd54c75c37b540sberlin * See the License for the specific language governing permissions and
144e11457f34addf5d10fe0c31cefd54c75c37b540sberlin * limitations under the License.
154e11457f34addf5d10fe0c31cefd54c75c37b540sberlin */
164e11457f34addf5d10fe0c31cefd54c75c37b540sberlin
174e11457f34addf5d10fe0c31cefd54c75c37b540sberlinpackage com.google.inject.multibindings;
184e11457f34addf5d10fe0c31cefd54c75c37b540sberlin
194e11457f34addf5d10fe0c31cefd54c75c37b540sberlinimport static com.google.inject.multibindings.MapBinder.entryOfProviderOf;
204e11457f34addf5d10fe0c31cefd54c75c37b540sberlinimport static com.google.inject.multibindings.MapBinder.mapOf;
213338a48b6abb9a0641dc3c2c169bb59b9960dc15Sam Berlinimport static com.google.inject.multibindings.MapBinder.mapOfJavaxProviderOf;
224e11457f34addf5d10fe0c31cefd54c75c37b540sberlinimport static com.google.inject.multibindings.MapBinder.mapOfProviderOf;
234e11457f34addf5d10fe0c31cefd54c75c37b540sberlinimport static com.google.inject.multibindings.MapBinder.mapOfSetOfProviderOf;
24bed1413751f54dc6804dcb2a4c28300081788603flanimport static com.google.inject.multibindings.Multibinder.collectionOfJavaxProvidersOf;
254faa20e3081448792933834aedfe972add806292Sam Berlinimport static com.google.inject.multibindings.Multibinder.collectionOfProvidersOf;
264e11457f34addf5d10fe0c31cefd54c75c37b540sberlinimport static com.google.inject.multibindings.Multibinder.setOf;
271d3f8cd0a163c7439589d80e6377edbf2ca4b43dsamebimport static com.google.inject.multibindings.OptionalBinder.javaOptionalOfJavaxProvider;
281d3f8cd0a163c7439589d80e6377edbf2ca4b43dsamebimport static com.google.inject.multibindings.OptionalBinder.javaOptionalOfProvider;
29af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruberimport static com.google.inject.multibindings.OptionalBinder.optionalOfJavaxProvider;
30af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruberimport static com.google.inject.multibindings.OptionalBinder.optionalOfProvider;
314e11457f34addf5d10fe0c31cefd54c75c37b540sberlinimport static com.google.inject.multibindings.SpiUtils.BindType.INSTANCE;
324e11457f34addf5d10fe0c31cefd54c75c37b540sberlinimport static com.google.inject.multibindings.SpiUtils.BindType.LINKED;
334e11457f34addf5d10fe0c31cefd54c75c37b540sberlinimport static com.google.inject.multibindings.SpiUtils.BindType.PROVIDER_INSTANCE;
34af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruberimport static com.google.inject.multibindings.SpiUtils.BindType.PROVIDER_KEY;
354e11457f34addf5d10fe0c31cefd54c75c37b540sberlinimport static com.google.inject.multibindings.SpiUtils.VisitType.BOTH;
364e11457f34addf5d10fe0c31cefd54c75c37b540sberlinimport static com.google.inject.multibindings.SpiUtils.VisitType.INJECTOR;
374e11457f34addf5d10fe0c31cefd54c75c37b540sberlinimport static com.google.inject.multibindings.SpiUtils.VisitType.MODULE;
384e11457f34addf5d10fe0c31cefd54c75c37b540sberlinimport static junit.framework.Assert.assertEquals;
394faa20e3081448792933834aedfe972add806292Sam Berlinimport static junit.framework.Assert.assertFalse;
404e11457f34addf5d10fe0c31cefd54c75c37b540sberlinimport static junit.framework.Assert.assertNotNull;
41af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruberimport static junit.framework.Assert.assertNull;
424e11457f34addf5d10fe0c31cefd54c75c37b540sberlinimport static junit.framework.Assert.assertTrue;
434e11457f34addf5d10fe0c31cefd54c75c37b540sberlinimport static junit.framework.Assert.fail;
444e11457f34addf5d10fe0c31cefd54c75c37b540sberlin
45af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruberimport com.google.common.base.Objects;
46af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruberimport com.google.common.base.Optional;
47af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruberimport com.google.common.collect.ImmutableMap;
486c69bcf53d4122b0f05f44783c0d8a61afd83911Christian Edward Gruberimport com.google.common.collect.ImmutableSet;
49b7a02b02d81c830d148355c90bc309bcd66fb592sberlinimport com.google.common.collect.Lists;
50c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlinimport com.google.common.collect.Maps;
51c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlinimport com.google.common.collect.Multimap;
52c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlinimport com.google.common.collect.MultimapBuilder;
53c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlinimport com.google.common.collect.Sets;
544e11457f34addf5d10fe0c31cefd54c75c37b540sberlinimport com.google.inject.Binding;
554e11457f34addf5d10fe0c31cefd54c75c37b540sberlinimport com.google.inject.Guice;
564e11457f34addf5d10fe0c31cefd54c75c37b540sberlinimport com.google.inject.Injector;
574e11457f34addf5d10fe0c31cefd54c75c37b540sberlinimport com.google.inject.Key;
584e11457f34addf5d10fe0c31cefd54c75c37b540sberlinimport com.google.inject.Module;
59af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruberimport com.google.inject.Provider;
604e11457f34addf5d10fe0c31cefd54c75c37b540sberlinimport com.google.inject.TypeLiteral;
61c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlinimport com.google.inject.multibindings.Indexer.IndexedBinding;
62c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlinimport com.google.inject.multibindings.MapBinder.RealMapBinder.ProviderMapEntry;
63af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruberimport com.google.inject.multibindings.OptionalBinder.Source;
644e11457f34addf5d10fe0c31cefd54c75c37b540sberlinimport com.google.inject.spi.DefaultBindingTargetVisitor;
654e11457f34addf5d10fe0c31cefd54c75c37b540sberlinimport com.google.inject.spi.Element;
664e11457f34addf5d10fe0c31cefd54c75c37b540sberlinimport com.google.inject.spi.Elements;
674e11457f34addf5d10fe0c31cefd54c75c37b540sberlinimport com.google.inject.spi.InstanceBinding;
684e11457f34addf5d10fe0c31cefd54c75c37b540sberlinimport com.google.inject.spi.LinkedKeyBinding;
694e11457f34addf5d10fe0c31cefd54c75c37b540sberlinimport com.google.inject.spi.ProviderInstanceBinding;
70af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruberimport com.google.inject.spi.ProviderKeyBinding;
714e11457f34addf5d10fe0c31cefd54c75c37b540sberlinimport com.google.inject.spi.ProviderLookup;
724e11457f34addf5d10fe0c31cefd54c75c37b540sberlin
73b7a02b02d81c830d148355c90bc309bcd66fb592sberlinimport java.util.HashSet;
74b7a02b02d81c830d148355c90bc309bcd66fb592sberlinimport java.util.List;
75b7a02b02d81c830d148355c90bc309bcd66fb592sberlinimport java.util.Map;
76b7a02b02d81c830d148355c90bc309bcd66fb592sberlinimport java.util.Set;
77b7a02b02d81c830d148355c90bc309bcd66fb592sberlin
784e11457f34addf5d10fe0c31cefd54c75c37b540sberlin/**
794e11457f34addf5d10fe0c31cefd54c75c37b540sberlin * Utilities for testing the Multibinder & MapBinder extension SPI.
804e11457f34addf5d10fe0c31cefd54c75c37b540sberlin *
814e11457f34addf5d10fe0c31cefd54c75c37b540sberlin * @author sameb@google.com (Sam Berlin)
824e11457f34addf5d10fe0c31cefd54c75c37b540sberlin */
834e11457f34addf5d10fe0c31cefd54c75c37b540sberlinpublic class SpiUtils {
844e11457f34addf5d10fe0c31cefd54c75c37b540sberlin
851d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb  private static final boolean HAS_JAVA_OPTIONAL;
861d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb  static {
871d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb    Class<?> optional = null;
881d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb    try {
891d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb      optional = Class.forName("java.util.Optional");
901d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb    } catch (ClassNotFoundException ignored) {}
911d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb    HAS_JAVA_OPTIONAL = optional != null;
921d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb  }
931d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb
944e11457f34addf5d10fe0c31cefd54c75c37b540sberlin  /** The kind of test we should perform.  A live Injector, a raw Elements (Module) test, or both. */
954e11457f34addf5d10fe0c31cefd54c75c37b540sberlin  enum VisitType { INJECTOR, MODULE, BOTH }
964e11457f34addf5d10fe0c31cefd54c75c37b540sberlin
974e11457f34addf5d10fe0c31cefd54c75c37b540sberlin  /**
984e11457f34addf5d10fe0c31cefd54c75c37b540sberlin   * Asserts that MapBinderBinding visitors for work correctly.
994e11457f34addf5d10fe0c31cefd54c75c37b540sberlin   *
1004e11457f34addf5d10fe0c31cefd54c75c37b540sberlin   * @param <T> The type of the binding
1014e11457f34addf5d10fe0c31cefd54c75c37b540sberlin   * @param mapKey The key the map belongs to.
1024e11457f34addf5d10fe0c31cefd54c75c37b540sberlin   * @param keyType the TypeLiteral of the key of the map
1034e11457f34addf5d10fe0c31cefd54c75c37b540sberlin   * @param valueType the TypeLiteral of the value of the map
1044e11457f34addf5d10fe0c31cefd54c75c37b540sberlin   * @param modules The modules that define the mapbindings
1054e11457f34addf5d10fe0c31cefd54c75c37b540sberlin   * @param visitType The kind of test we should perform.  A live Injector, a raw Elements (Module) test, or both.
1064e11457f34addf5d10fe0c31cefd54c75c37b540sberlin   * @param allowDuplicates If duplicates are allowed.
1074e11457f34addf5d10fe0c31cefd54c75c37b540sberlin   * @param expectedMapBindings The number of other mapbinders we expect to see.
1084e11457f34addf5d10fe0c31cefd54c75c37b540sberlin   * @param results The kind of bindings contained in the mapbinder.
1094e11457f34addf5d10fe0c31cefd54c75c37b540sberlin   */
1104e11457f34addf5d10fe0c31cefd54c75c37b540sberlin  static <T> void assertMapVisitor(Key<T> mapKey, TypeLiteral<?> keyType, TypeLiteral<?> valueType,
1114e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      Iterable<? extends Module> modules, VisitType visitType, boolean allowDuplicates,
1124e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      int expectedMapBindings, MapResult... results) {
1134e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    if(visitType == null) {
1144e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      fail("must test something");
1154e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    }
1164e11457f34addf5d10fe0c31cefd54c75c37b540sberlin
1174e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    if (visitType == BOTH || visitType == INJECTOR) {
1184e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      mapInjectorTest(mapKey, keyType, valueType, modules, allowDuplicates, expectedMapBindings,
1194e11457f34addf5d10fe0c31cefd54c75c37b540sberlin          results);
1204e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    }
1214e11457f34addf5d10fe0c31cefd54c75c37b540sberlin
1224e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    if (visitType == BOTH || visitType == MODULE) {
1234e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      mapModuleTest(mapKey, keyType, valueType, modules, allowDuplicates, expectedMapBindings,
1244e11457f34addf5d10fe0c31cefd54c75c37b540sberlin          results);
1254e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    }
1264e11457f34addf5d10fe0c31cefd54c75c37b540sberlin  }
1274e11457f34addf5d10fe0c31cefd54c75c37b540sberlin
1284e11457f34addf5d10fe0c31cefd54c75c37b540sberlin  @SuppressWarnings("unchecked")
1294e11457f34addf5d10fe0c31cefd54c75c37b540sberlin  private static <T> void mapInjectorTest(Key<T> mapKey, TypeLiteral<?> keyType,
1304e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      TypeLiteral<?> valueType, Iterable<? extends Module> modules, boolean allowDuplicates,
1314e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      int expectedMapBindings, MapResult... results) {
1324e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    Injector injector = Guice.createInjector(modules);
1334e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    Visitor<T> visitor = new Visitor<T>();
1344e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    Binding<T> mapBinding = injector.getBinding(mapKey);
1354e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    MapBinderBinding<T> mapbinder = (MapBinderBinding<T>)mapBinding.acceptTargetVisitor(visitor);
1364e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    assertNotNull(mapbinder);
1374e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    assertEquals(keyType, mapbinder.getKeyTypeLiteral());
1384e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    assertEquals(valueType, mapbinder.getValueTypeLiteral());
1394e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    assertEquals(allowDuplicates, mapbinder.permitsDuplicates());
1404e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    List<Map.Entry<?, Binding<?>>> entries = Lists.newArrayList(mapbinder.getEntries());
1414e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    List<MapResult> mapResults = Lists.newArrayList(results);
1424e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    assertEquals("wrong entries, expected: " + mapResults + ", but was: " + entries,
1434e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        mapResults.size(), entries.size());
1444e11457f34addf5d10fe0c31cefd54c75c37b540sberlin
1454e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    for(MapResult result : mapResults) {
1464e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      Map.Entry<?, Binding<?>> found = null;
1474e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      for(Map.Entry<?, Binding<?>> entry : entries) {
1484e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        Object key = entry.getKey();
1494e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        Binding<?> value = entry.getValue();
150af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        if(key.equals(result.k) && matches(value, result.v)) {
151af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber          found = entry;
1524e11457f34addf5d10fe0c31cefd54c75c37b540sberlin          break;
1534e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        }
1544e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      }
1554e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      if(found == null) {
1564e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        fail("Could not find entry: " + result + " in remaining entries: " + entries);
1574e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      } else {
158af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        assertTrue("mapBinder doesn't contain: " + found.getValue(),
159af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber            mapbinder.containsElement(found.getValue()));
1604e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        entries.remove(found);
1614e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      }
1624e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    }
1634e11457f34addf5d10fe0c31cefd54c75c37b540sberlin
1644e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    if(!entries.isEmpty()) {
1654e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      fail("Found all entries of: " + mapResults + ", but more were left over: " + entries);
1664e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    }
1674e11457f34addf5d10fe0c31cefd54c75c37b540sberlin
1683338a48b6abb9a0641dc3c2c169bb59b9960dc15Sam Berlin    Key<?> mapOfJavaxProvider = mapKey.ofType(mapOfJavaxProviderOf(keyType, valueType));
1693338a48b6abb9a0641dc3c2c169bb59b9960dc15Sam Berlin    Key<?> mapOfProvider = mapKey.ofType(mapOfProviderOf(keyType, valueType));
1703338a48b6abb9a0641dc3c2c169bb59b9960dc15Sam Berlin    Key<?> mapOfSetOfProvider = mapKey.ofType(mapOfSetOfProviderOf(keyType, valueType));
1713338a48b6abb9a0641dc3c2c169bb59b9960dc15Sam Berlin    Key<?> mapOfSet = mapKey.ofType(mapOf(keyType, setOf(valueType)));
1723338a48b6abb9a0641dc3c2c169bb59b9960dc15Sam Berlin    Key<?> setOfEntry = mapKey.ofType(setOf(entryOfProviderOf(keyType, valueType)));
173615fd2bc2397e2966d682759aae2aad7e8a3db1eflan    Key<?> collectionOfProvidersOfEntryOfProvider =
174615fd2bc2397e2966d682759aae2aad7e8a3db1eflan        mapKey.ofType(collectionOfProvidersOf(entryOfProviderOf(keyType, valueType)));
175bed1413751f54dc6804dcb2a4c28300081788603flan    Key<?> collectionOfJavaxProvidersOfEntryOfProvider =
176bed1413751f54dc6804dcb2a4c28300081788603flan        mapKey.ofType(collectionOfJavaxProvidersOf(entryOfProviderOf(keyType, valueType)));
1774e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    boolean entrySetMatch = false;
1783338a48b6abb9a0641dc3c2c169bb59b9960dc15Sam Berlin    boolean mapJavaxProviderMatch = false;
1794e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    boolean mapProviderMatch = false;
1804e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    boolean mapSetMatch = false;
1814e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    boolean mapSetProviderMatch = false;
182615fd2bc2397e2966d682759aae2aad7e8a3db1eflan    boolean collectionOfProvidersOfEntryOfProviderMatch = false;
183bed1413751f54dc6804dcb2a4c28300081788603flan    boolean collectionOfJavaxProvidersOfEntryOfProviderMatch = false;
1844e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    List<Object> otherMapBindings = Lists.newArrayList();
1854e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    List<Binding> otherMatches = Lists.newArrayList();
186c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin    Multimap<Object, IndexedBinding> indexedEntries =
187c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin        MultimapBuilder.hashKeys().hashSetValues().build();
188c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin    Indexer indexer = new Indexer(injector);
189c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin    int duplicates = 0;
1904e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    for(Binding b : injector.getAllBindings().values()) {
1914e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      boolean contains = mapbinder.containsElement(b);
1924e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      Object visited = b.acceptTargetVisitor(visitor);
1934e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      if(visited instanceof MapBinderBinding) {
1944e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        if(visited.equals(mapbinder)) {
1954e11457f34addf5d10fe0c31cefd54c75c37b540sberlin          assertTrue(contains);
1964e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        } else {
1974e11457f34addf5d10fe0c31cefd54c75c37b540sberlin          otherMapBindings.add(visited);
1984e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        }
1994e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      } else if(b.getKey().equals(mapOfProvider)) {
2004e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        assertTrue(contains);
2014e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        mapProviderMatch = true;
2023338a48b6abb9a0641dc3c2c169bb59b9960dc15Sam Berlin      } else if (b.getKey().equals(mapOfJavaxProvider)) {
2033338a48b6abb9a0641dc3c2c169bb59b9960dc15Sam Berlin        assertTrue(contains);
2043338a48b6abb9a0641dc3c2c169bb59b9960dc15Sam Berlin        mapJavaxProviderMatch = true;
2054e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      } else if(b.getKey().equals(mapOfSet)) {
2064e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        assertTrue(contains);
2074e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        mapSetMatch = true;
2084e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      } else if(b.getKey().equals(mapOfSetOfProvider)) {
2094e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        assertTrue(contains);
2104e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        mapSetProviderMatch = true;
2114e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      } else if(b.getKey().equals(setOfEntry)) {
2124e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        assertTrue(contains);
2134e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        entrySetMatch = true;
2144e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        // Validate that this binding is also a MultibinderBinding.
2154e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        assertTrue(b.acceptTargetVisitor(visitor) instanceof MultibinderBinding);
216615fd2bc2397e2966d682759aae2aad7e8a3db1eflan      } else if(b.getKey().equals(collectionOfProvidersOfEntryOfProvider)) {
217615fd2bc2397e2966d682759aae2aad7e8a3db1eflan        assertTrue(contains);
218615fd2bc2397e2966d682759aae2aad7e8a3db1eflan        collectionOfProvidersOfEntryOfProviderMatch = true;
219bed1413751f54dc6804dcb2a4c28300081788603flan      } else if(b.getKey().equals(collectionOfJavaxProvidersOfEntryOfProvider)) {
220bed1413751f54dc6804dcb2a4c28300081788603flan        assertTrue(contains);
221bed1413751f54dc6804dcb2a4c28300081788603flan        collectionOfJavaxProvidersOfEntryOfProviderMatch = true;
2224e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      } else if (contains) {
223c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin        if (b instanceof ProviderInstanceBinding) {
224c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin          ProviderInstanceBinding<?> pib = (ProviderInstanceBinding<?>)b;
225c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin          if (pib.getUserSuppliedProvider() instanceof ProviderMapEntry) {
226e5abfb2c1e4ff087bafa5d925fe0598d968b803bSam Berlin            // weird casting required to workaround compilation issues with jdk6
227e5abfb2c1e4ff087bafa5d925fe0598d968b803bSam Berlin            ProviderMapEntry<?, ?> pme =
228e5abfb2c1e4ff087bafa5d925fe0598d968b803bSam Berlin                (ProviderMapEntry<?, ?>) (Provider) pib.getUserSuppliedProvider();
229c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin            Binding<?> valueBinding = injector.getBinding(pme.getValueKey());
230c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin            if (indexer.isIndexable(valueBinding)
231c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin                && !indexedEntries.put(pme.getKey(), valueBinding.acceptTargetVisitor(indexer))) {
232c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin              duplicates++;
233c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin            }
234c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin          }
235c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin        }
2364e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        otherMatches.add(b);
2374e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      }
2384e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    }
2394e11457f34addf5d10fe0c31cefd54c75c37b540sberlin
2404e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    int sizeOfOther = otherMatches.size();
2414e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    if(allowDuplicates) {
2424e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      sizeOfOther--; // account for 1 duplicate binding
2434e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    }
244615fd2bc2397e2966d682759aae2aad7e8a3db1eflan    // Multiply by two because each has a value and Map.Entry.
245615fd2bc2397e2966d682759aae2aad7e8a3db1eflan    int expectedSize = 2 * (mapResults.size() + duplicates);
246615fd2bc2397e2966d682759aae2aad7e8a3db1eflan    assertEquals("Incorrect other matches: " + otherMatches, expectedSize, sizeOfOther);
2474e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    assertTrue(entrySetMatch);
2484e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    assertTrue(mapProviderMatch);
2493338a48b6abb9a0641dc3c2c169bb59b9960dc15Sam Berlin    assertTrue(mapJavaxProviderMatch);
250615fd2bc2397e2966d682759aae2aad7e8a3db1eflan    assertTrue(collectionOfProvidersOfEntryOfProviderMatch);
251bed1413751f54dc6804dcb2a4c28300081788603flan    assertTrue(collectionOfJavaxProvidersOfEntryOfProviderMatch);
2524e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    assertEquals(allowDuplicates, mapSetMatch);
2534e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    assertEquals(allowDuplicates, mapSetProviderMatch);
2544e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    assertEquals("other MapBindings found: " + otherMapBindings, expectedMapBindings,
2554e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        otherMapBindings.size());
2564e11457f34addf5d10fe0c31cefd54c75c37b540sberlin  }
2574e11457f34addf5d10fe0c31cefd54c75c37b540sberlin
2584e11457f34addf5d10fe0c31cefd54c75c37b540sberlin  @SuppressWarnings("unchecked")
2594e11457f34addf5d10fe0c31cefd54c75c37b540sberlin  private static <T> void mapModuleTest(Key<T> mapKey, TypeLiteral<?> keyType,
2604e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      TypeLiteral<?> valueType, Iterable<? extends Module> modules, boolean allowDuplicates,
2614e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      int expectedMapBindings, MapResult... results) {
2626c69bcf53d4122b0f05f44783c0d8a61afd83911Christian Edward Gruber    Set<Element> elements = ImmutableSet.copyOf(Elements.getElements(modules));
2634e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    Visitor<T> visitor = new Visitor<T>();
2644e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    MapBinderBinding<T> mapbinder = null;
265c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin    Map<Key<?>, Binding<?>> keyMap = Maps.newHashMap();
2664e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    for(Element element : elements) {
267c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin      if(element instanceof Binding) {
268c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin        Binding<?> binding = (Binding<?>)element;
269c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin        keyMap.put(binding.getKey(), binding);
270c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin        if (binding.getKey().equals(mapKey)) {
271c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin          mapbinder = (MapBinderBinding<T>)((Binding<T>)binding).acceptTargetVisitor(visitor);
272c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin        }
2734e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      }
2744e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    }
2754e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    assertNotNull(mapbinder);
2764e11457f34addf5d10fe0c31cefd54c75c37b540sberlin
2774e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    assertEquals(keyType, mapbinder.getKeyTypeLiteral());
2784e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    assertEquals(valueType, mapbinder.getValueTypeLiteral());
2794e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    List<MapResult> mapResults = Lists.newArrayList(results);
2804e11457f34addf5d10fe0c31cefd54c75c37b540sberlin
2813338a48b6abb9a0641dc3c2c169bb59b9960dc15Sam Berlin    Key<?> mapOfProvider = mapKey.ofType(mapOfProviderOf(keyType, valueType));
282615fd2bc2397e2966d682759aae2aad7e8a3db1eflan    Key<?> mapOfJavaxProvider = mapKey.ofType(mapOfJavaxProviderOf(keyType, valueType));
2833338a48b6abb9a0641dc3c2c169bb59b9960dc15Sam Berlin    Key<?> mapOfSetOfProvider = mapKey.ofType(mapOfSetOfProviderOf(keyType, valueType));
2843338a48b6abb9a0641dc3c2c169bb59b9960dc15Sam Berlin    Key<?> mapOfSet = mapKey.ofType(mapOf(keyType, setOf(valueType)));
2853338a48b6abb9a0641dc3c2c169bb59b9960dc15Sam Berlin    Key<?> setOfEntry = mapKey.ofType(setOf(entryOfProviderOf(keyType, valueType)));
2864faa20e3081448792933834aedfe972add806292Sam Berlin    Key<?> collectionOfProvidersOfEntry =
2874faa20e3081448792933834aedfe972add806292Sam Berlin        mapKey.ofType(collectionOfProvidersOf(entryOfProviderOf(keyType, valueType)));
288bed1413751f54dc6804dcb2a4c28300081788603flan    Key<?> collectionOfJavaxProvidersOfEntry =
289bed1413751f54dc6804dcb2a4c28300081788603flan        mapKey.ofType(collectionOfJavaxProvidersOf(entryOfProviderOf(keyType, valueType)));
2904e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    boolean entrySetMatch = false;
2914e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    boolean mapProviderMatch = false;
292615fd2bc2397e2966d682759aae2aad7e8a3db1eflan    boolean mapJavaxProviderMatch = false;
293615fd2bc2397e2966d682759aae2aad7e8a3db1eflan    boolean mapSetMatch = false;
2944e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    boolean mapSetProviderMatch = false;
295bed1413751f54dc6804dcb2a4c28300081788603flan    boolean collectionOfProvidersOfEntryMatch = false;
296bed1413751f54dc6804dcb2a4c28300081788603flan    boolean collectionOfJavaxProvidersOfEntryMatch = false;
2974e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    List<Object> otherMapBindings = Lists.newArrayList();
2984e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    List<Element> otherMatches = Lists.newArrayList();
2994e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    List<Element> otherElements = Lists.newArrayList();
300c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin    Indexer indexer = new Indexer(null);
301c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin    Multimap<Object, IndexedBinding> indexedEntries =
302c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin        MultimapBuilder.hashKeys().hashSetValues().build();
303c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin    int duplicates = 0;
3044e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    for(Element element : elements) {
3054e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      boolean contains = mapbinder.containsElement(element);
3064e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      if(!contains) {
3074e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        otherElements.add(element);
3084e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      }
3094e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      boolean matched = false;
3104e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      Key key = null;
3114e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      Binding b = null;
3124e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      if(element instanceof Binding) {
3134e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        b = (Binding)element;
314c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin        if (b instanceof ProviderInstanceBinding) {
315c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin          ProviderInstanceBinding<?> pb = (ProviderInstanceBinding<?>) b;
316c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin          if (pb.getUserSuppliedProvider() instanceof ProviderMapEntry) {
317e5abfb2c1e4ff087bafa5d925fe0598d968b803bSam Berlin            // weird casting required to workaround jdk6 compilation problems
318e5abfb2c1e4ff087bafa5d925fe0598d968b803bSam Berlin            ProviderMapEntry<?, ?> pme =
319e5abfb2c1e4ff087bafa5d925fe0598d968b803bSam Berlin                (ProviderMapEntry<?, ?>) (Provider) pb.getUserSuppliedProvider();
320c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin            Binding<?> valueBinding = keyMap.get(pme.getValueKey());
321c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin            if (indexer.isIndexable(valueBinding)
322c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin                && !indexedEntries.put(pme.getKey(), valueBinding.acceptTargetVisitor(indexer))) {
323c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin              duplicates++;
324c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin            }
325c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin          }
326c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin        }
327c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin
3284e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        key = b.getKey();
3294e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        Object visited = b.acceptTargetVisitor(visitor);
3304e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        if(visited instanceof MapBinderBinding) {
3314e11457f34addf5d10fe0c31cefd54c75c37b540sberlin          matched = true;
3324e11457f34addf5d10fe0c31cefd54c75c37b540sberlin          if(visited.equals(mapbinder)) {
3334e11457f34addf5d10fe0c31cefd54c75c37b540sberlin            assertTrue(contains);
3344e11457f34addf5d10fe0c31cefd54c75c37b540sberlin          } else {
3354e11457f34addf5d10fe0c31cefd54c75c37b540sberlin            otherMapBindings.add(visited);
3364e11457f34addf5d10fe0c31cefd54c75c37b540sberlin          }
3374e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        }
3384e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      } else if(element instanceof ProviderLookup) {
3394e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        key = ((ProviderLookup)element).getKey();
3404e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      }
3414e11457f34addf5d10fe0c31cefd54c75c37b540sberlin
3424e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      if(!matched && key != null) {
3434e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        if(key.equals(mapOfProvider)) {
3444e11457f34addf5d10fe0c31cefd54c75c37b540sberlin          matched = true;
3454e11457f34addf5d10fe0c31cefd54c75c37b540sberlin          assertTrue(contains);
3464e11457f34addf5d10fe0c31cefd54c75c37b540sberlin          mapProviderMatch = true;
347615fd2bc2397e2966d682759aae2aad7e8a3db1eflan        } else if(key.equals(mapOfJavaxProvider)) {
348615fd2bc2397e2966d682759aae2aad7e8a3db1eflan          matched = true;
349615fd2bc2397e2966d682759aae2aad7e8a3db1eflan          assertTrue(contains);
350615fd2bc2397e2966d682759aae2aad7e8a3db1eflan          mapJavaxProviderMatch = true;
3514e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        } else if(key.equals(mapOfSet)) {
3524e11457f34addf5d10fe0c31cefd54c75c37b540sberlin          matched = true;
3534e11457f34addf5d10fe0c31cefd54c75c37b540sberlin          assertTrue(contains);
3544e11457f34addf5d10fe0c31cefd54c75c37b540sberlin          mapSetMatch = true;
3554e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        } else if(key.equals(mapOfSetOfProvider)) {
3564e11457f34addf5d10fe0c31cefd54c75c37b540sberlin          matched = true;
3574e11457f34addf5d10fe0c31cefd54c75c37b540sberlin          assertTrue(contains);
3584e11457f34addf5d10fe0c31cefd54c75c37b540sberlin          mapSetProviderMatch = true;
3594e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        } else if(key.equals(setOfEntry)) {
3604e11457f34addf5d10fe0c31cefd54c75c37b540sberlin          matched = true;
3614e11457f34addf5d10fe0c31cefd54c75c37b540sberlin          assertTrue(contains);
3624e11457f34addf5d10fe0c31cefd54c75c37b540sberlin          entrySetMatch = true;
3634e11457f34addf5d10fe0c31cefd54c75c37b540sberlin          // Validate that this binding is also a MultibinderBinding.
3644e11457f34addf5d10fe0c31cefd54c75c37b540sberlin          if(b != null) {
3654e11457f34addf5d10fe0c31cefd54c75c37b540sberlin            assertTrue(b.acceptTargetVisitor(visitor) instanceof MultibinderBinding);
3664e11457f34addf5d10fe0c31cefd54c75c37b540sberlin          }
3674faa20e3081448792933834aedfe972add806292Sam Berlin        } else if(key.equals(collectionOfProvidersOfEntry)) {
3684faa20e3081448792933834aedfe972add806292Sam Berlin          matched = true;
3694faa20e3081448792933834aedfe972add806292Sam Berlin          assertTrue(contains);
370bed1413751f54dc6804dcb2a4c28300081788603flan          collectionOfProvidersOfEntryMatch = true;
371bed1413751f54dc6804dcb2a4c28300081788603flan        } else if(key.equals(collectionOfJavaxProvidersOfEntry)) {
372bed1413751f54dc6804dcb2a4c28300081788603flan          matched = true;
373bed1413751f54dc6804dcb2a4c28300081788603flan          assertTrue(contains);
374bed1413751f54dc6804dcb2a4c28300081788603flan          collectionOfJavaxProvidersOfEntryMatch = true;
3754e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        }
3764e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      }
3774e11457f34addf5d10fe0c31cefd54c75c37b540sberlin
3784faa20e3081448792933834aedfe972add806292Sam Berlin      if (!matched && contains) {
3794e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        otherMatches.add(element);
3804e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      }
3814e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    }
3824e11457f34addf5d10fe0c31cefd54c75c37b540sberlin
3834e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    int otherMatchesSize = otherMatches.size();
3844faa20e3081448792933834aedfe972add806292Sam Berlin    if (allowDuplicates) {
3854e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      otherMatchesSize--; // allow for 1 duplicate binding
3864e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    }
387615fd2bc2397e2966d682759aae2aad7e8a3db1eflan    // Multiply by 3 because each has a value, ProviderLookup, and Map.Entry
388615fd2bc2397e2966d682759aae2aad7e8a3db1eflan    int expectedSize = (mapResults.size() + duplicates) * 3;
389615fd2bc2397e2966d682759aae2aad7e8a3db1eflan    assertEquals("incorrect number of contains, leftover matches: " + otherMatches,
390615fd2bc2397e2966d682759aae2aad7e8a3db1eflan        expectedSize, otherMatchesSize);
3914e11457f34addf5d10fe0c31cefd54c75c37b540sberlin
3924e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    assertTrue(entrySetMatch);
3934e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    assertTrue(mapProviderMatch);
394615fd2bc2397e2966d682759aae2aad7e8a3db1eflan    assertTrue(mapJavaxProviderMatch);
395bed1413751f54dc6804dcb2a4c28300081788603flan    assertTrue(collectionOfProvidersOfEntryMatch);
396bed1413751f54dc6804dcb2a4c28300081788603flan    assertTrue(collectionOfJavaxProvidersOfEntryMatch);
3974e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    assertEquals(allowDuplicates, mapSetMatch);
3984e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    assertEquals(allowDuplicates, mapSetProviderMatch);
3994e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    assertEquals("other MapBindings found: " + otherMapBindings, expectedMapBindings,
4004e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        otherMapBindings.size());
4014e11457f34addf5d10fe0c31cefd54c75c37b540sberlin
4024e11457f34addf5d10fe0c31cefd54c75c37b540sberlin     // Validate that we can construct an injector out of the remaining bindings.
4034e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    Guice.createInjector(Elements.getModule(otherElements));
4044e11457f34addf5d10fe0c31cefd54c75c37b540sberlin  }
4054e11457f34addf5d10fe0c31cefd54c75c37b540sberlin
4064e11457f34addf5d10fe0c31cefd54c75c37b540sberlin  /**
4074e11457f34addf5d10fe0c31cefd54c75c37b540sberlin   * Asserts that MultibinderBinding visitors work correctly.
4084e11457f34addf5d10fe0c31cefd54c75c37b540sberlin   *
4094e11457f34addf5d10fe0c31cefd54c75c37b540sberlin   * @param <T> The type of the binding
4104e11457f34addf5d10fe0c31cefd54c75c37b540sberlin   * @param setKey The key the set belongs to.
4114e11457f34addf5d10fe0c31cefd54c75c37b540sberlin   * @param elementType the TypeLiteral of the element
4124e11457f34addf5d10fe0c31cefd54c75c37b540sberlin   * @param modules The modules that define the multibindings
4134e11457f34addf5d10fe0c31cefd54c75c37b540sberlin   * @param visitType The kind of test we should perform.  A live Injector, a raw Elements (Module) test, or both.
4144e11457f34addf5d10fe0c31cefd54c75c37b540sberlin   * @param allowDuplicates If duplicates are allowed.
4154e11457f34addf5d10fe0c31cefd54c75c37b540sberlin   * @param expectedMultibindings The number of other multibinders we expect to see.
4164e11457f34addf5d10fe0c31cefd54c75c37b540sberlin   * @param results The kind of bindings contained in the multibinder.
4174e11457f34addf5d10fe0c31cefd54c75c37b540sberlin   */
4189c8b61815fa15ee3457b9c816afe24a6fdaf7014flan  static <T> void assertSetVisitor(Key<Set<T>> setKey, TypeLiteral<?> elementType,
4194e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      Iterable<? extends Module> modules, VisitType visitType, boolean allowDuplicates,
4204e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      int expectedMultibindings, BindResult... results) {
4214e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    if(visitType == null) {
4224e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      fail("must test something");
4234e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    }
4244e11457f34addf5d10fe0c31cefd54c75c37b540sberlin
4254e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    if(visitType == BOTH || visitType == INJECTOR) {
4269c8b61815fa15ee3457b9c816afe24a6fdaf7014flan      setInjectorTest(setKey, elementType, modules, allowDuplicates,
4274faa20e3081448792933834aedfe972add806292Sam Berlin          expectedMultibindings, results);
4284e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    }
4294e11457f34addf5d10fe0c31cefd54c75c37b540sberlin
4304e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    if(visitType == BOTH || visitType == MODULE) {
4319c8b61815fa15ee3457b9c816afe24a6fdaf7014flan      setModuleTest(setKey, elementType, modules, allowDuplicates,
4324faa20e3081448792933834aedfe972add806292Sam Berlin          expectedMultibindings, results);
4334e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    }
4344e11457f34addf5d10fe0c31cefd54c75c37b540sberlin  }
4354e11457f34addf5d10fe0c31cefd54c75c37b540sberlin
4364e11457f34addf5d10fe0c31cefd54c75c37b540sberlin  @SuppressWarnings("unchecked")
4379c8b61815fa15ee3457b9c816afe24a6fdaf7014flan  private static <T> void setInjectorTest(Key<Set<T>> setKey, TypeLiteral<?> elementType,
4384e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      Iterable<? extends Module> modules, boolean allowDuplicates, int otherMultibindings,
4394e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      BindResult... results) {
4409c8b61815fa15ee3457b9c816afe24a6fdaf7014flan    Key<?> collectionOfProvidersKey = setKey.ofType(collectionOfProvidersOf(elementType));
441bed1413751f54dc6804dcb2a4c28300081788603flan    Key<?> collectionOfJavaxProvidersKey =
442bed1413751f54dc6804dcb2a4c28300081788603flan        setKey.ofType(collectionOfJavaxProvidersOf(elementType));
4434e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    Injector injector = Guice.createInjector(modules);
4444faa20e3081448792933834aedfe972add806292Sam Berlin    Visitor<Set<T>> visitor = new Visitor<Set<T>>();
4454faa20e3081448792933834aedfe972add806292Sam Berlin    Binding<Set<T>> binding = injector.getBinding(setKey);
4464faa20e3081448792933834aedfe972add806292Sam Berlin    MultibinderBinding<Set<T>> multibinder =
4474faa20e3081448792933834aedfe972add806292Sam Berlin        (MultibinderBinding<Set<T>>)binding.acceptTargetVisitor(visitor);
4484e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    assertNotNull(multibinder);
4494e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    assertEquals(elementType, multibinder.getElementTypeLiteral());
4504e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    assertEquals(allowDuplicates, multibinder.permitsDuplicates());
4514e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    List<Binding<?>> elements = Lists.newArrayList(multibinder.getElements());
4524e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    List<BindResult> bindResults = Lists.newArrayList(results);
453c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin    assertEquals("wrong bind elements, expected: " + bindResults
454c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin        + ", but was: " + multibinder.getElements(),
4554e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        bindResults.size(), elements.size());
4564faa20e3081448792933834aedfe972add806292Sam Berlin
4574e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    for(BindResult result : bindResults) {
4584e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      Binding found = null;
4594e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      for(Binding item : elements) {
460af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        if (matches(item, result)) {
461af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber          found = item;
4624e11457f34addf5d10fe0c31cefd54c75c37b540sberlin          break;
4634e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        }
4644e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      }
4654e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      if(found == null) {
4664e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        fail("Could not find element: " + result + " in remaining elements: " + elements);
4674e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      } else {
4684e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        elements.remove(found);
4694e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      }
4704e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    }
4714e11457f34addf5d10fe0c31cefd54c75c37b540sberlin
4724e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    if(!elements.isEmpty()) {
4734e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      fail("Found all elements of: " + bindResults + ", but more were left over: " + elements);
4744e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    }
475c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin
4764e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    Set<Binding> setOfElements = new HashSet<Binding>(multibinder.getElements());
477c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin    Set<IndexedBinding> setOfIndexed = Sets.newHashSet();
478c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin    Indexer indexer = new Indexer(injector);
479c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin    for (Binding<?> oneBinding : setOfElements) {
480c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin      setOfIndexed.add(oneBinding.acceptTargetVisitor(indexer));
481c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin    }
482c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin
4834e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    List<Object> otherMultibinders = Lists.newArrayList();
4844e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    List<Binding> otherContains = Lists.newArrayList();
4854faa20e3081448792933834aedfe972add806292Sam Berlin    boolean collectionOfProvidersMatch = false;
486bed1413751f54dc6804dcb2a4c28300081788603flan    boolean collectionOfJavaxProvidersMatch = false;
4874e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    for(Binding b : injector.getAllBindings().values()) {
4884e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      boolean contains = multibinder.containsElement(b);
4894faa20e3081448792933834aedfe972add806292Sam Berlin      Key key = b.getKey();
4904e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      Object visited = b.acceptTargetVisitor(visitor);
4914e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      if(visited != null) {
4924e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        if(visited.equals(multibinder)) {
4934e11457f34addf5d10fe0c31cefd54c75c37b540sberlin          assertTrue(contains);
4944e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        } else {
4954e11457f34addf5d10fe0c31cefd54c75c37b540sberlin          otherMultibinders.add(visited);
4964e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        }
4974e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      } else if(setOfElements.contains(b)) {
4984e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        assertTrue(contains);
4994faa20e3081448792933834aedfe972add806292Sam Berlin      } else if (key.equals(collectionOfProvidersKey)) {
5004faa20e3081448792933834aedfe972add806292Sam Berlin        assertTrue(contains);
5014faa20e3081448792933834aedfe972add806292Sam Berlin        collectionOfProvidersMatch = true;
502bed1413751f54dc6804dcb2a4c28300081788603flan      } else if (key.equals(collectionOfJavaxProvidersKey)) {
503bed1413751f54dc6804dcb2a4c28300081788603flan        assertTrue(contains);
504bed1413751f54dc6804dcb2a4c28300081788603flan        collectionOfJavaxProvidersMatch = true;
5054faa20e3081448792933834aedfe972add806292Sam Berlin      } else if (contains) {
506c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin        if (!indexer.isIndexable(b) || !setOfIndexed.contains(b.acceptTargetVisitor(indexer))) {
507c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin          otherContains.add(b);
508c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin        }
5094e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      }
5104e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    }
5114faa20e3081448792933834aedfe972add806292Sam Berlin
5124faa20e3081448792933834aedfe972add806292Sam Berlin    assertTrue(collectionOfProvidersMatch);
513bed1413751f54dc6804dcb2a4c28300081788603flan    assertTrue(collectionOfJavaxProvidersMatch);
5144faa20e3081448792933834aedfe972add806292Sam Berlin
5154e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    if(allowDuplicates) {
5164e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      assertEquals("contained more than it should: " + otherContains, 1, otherContains.size());
5174e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    } else {
5184e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      assertTrue("contained more than it should: " + otherContains, otherContains.isEmpty());
5194e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    }
5204e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    assertEquals("other multibindings found: " + otherMultibinders, otherMultibindings,
5214e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        otherMultibinders.size());
5224e11457f34addf5d10fe0c31cefd54c75c37b540sberlin
5234e11457f34addf5d10fe0c31cefd54c75c37b540sberlin  }
5244e11457f34addf5d10fe0c31cefd54c75c37b540sberlin
5254e11457f34addf5d10fe0c31cefd54c75c37b540sberlin  @SuppressWarnings("unchecked")
5269c8b61815fa15ee3457b9c816afe24a6fdaf7014flan  private static <T> void setModuleTest(Key<Set<T>> setKey, TypeLiteral<?> elementType,
5274e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      Iterable<? extends Module> modules, boolean allowDuplicates, int otherMultibindings,
5284e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      BindResult... results) {
5299c8b61815fa15ee3457b9c816afe24a6fdaf7014flan    Key<?> collectionOfProvidersKey = setKey.ofType(collectionOfProvidersOf(elementType));
530bed1413751f54dc6804dcb2a4c28300081788603flan    Key<?> collectionOfJavaxProvidersKey =
531bed1413751f54dc6804dcb2a4c28300081788603flan        setKey.ofType(collectionOfJavaxProvidersOf(elementType));
5324e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    List<BindResult> bindResults = Lists.newArrayList(results);
5334e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    List<Element> elements = Elements.getElements(modules);
5344e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    Visitor<T> visitor = new Visitor<T>();
5354faa20e3081448792933834aedfe972add806292Sam Berlin    MultibinderBinding<Set<T>> multibinder = null;
5364e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    for(Element element : elements) {
5374e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      if(element instanceof Binding && ((Binding)element).getKey().equals(setKey)) {
5384faa20e3081448792933834aedfe972add806292Sam Berlin        multibinder = (MultibinderBinding<Set<T>>)((Binding)element).acceptTargetVisitor(visitor);
5394e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        break;
5404e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      }
5414e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    }
5424e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    assertNotNull(multibinder);
5434e11457f34addf5d10fe0c31cefd54c75c37b540sberlin
5444e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    assertEquals(elementType, multibinder.getElementTypeLiteral());
5454e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    List<Object> otherMultibinders = Lists.newArrayList();
5464e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    Set<Element> otherContains = new HashSet<Element>();
5474e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    List<Element> otherElements = Lists.newArrayList();
548c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin    int duplicates = 0;
549c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin    Set<IndexedBinding> setOfIndexed = Sets.newHashSet();
550c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin    Indexer indexer = new Indexer(null);
5514faa20e3081448792933834aedfe972add806292Sam Berlin    boolean collectionOfProvidersMatch = false;
552bed1413751f54dc6804dcb2a4c28300081788603flan    boolean collectionOfJavaxProvidersMatch = false;
5534e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    for(Element element : elements) {
5544e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      boolean contains = multibinder.containsElement(element);
5554e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      if(!contains) {
5564e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        otherElements.add(element);
5574e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      }
5584e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      boolean matched = false;
5594faa20e3081448792933834aedfe972add806292Sam Berlin      Key key = null;
5604e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      if(element instanceof Binding) {
5614e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        Binding binding = (Binding)element;
562c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin        if (indexer.isIndexable(binding)
563c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin            && !setOfIndexed.add((IndexedBinding) binding.acceptTargetVisitor(indexer))) {
564c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin          duplicates++;
565c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin        }
5664faa20e3081448792933834aedfe972add806292Sam Berlin        key = binding.getKey();
5674e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        Object visited = binding.acceptTargetVisitor(visitor);
5684e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        if(visited != null) {
5694e11457f34addf5d10fe0c31cefd54c75c37b540sberlin          matched = true;
5704e11457f34addf5d10fe0c31cefd54c75c37b540sberlin          if(visited.equals(multibinder)) {
5714e11457f34addf5d10fe0c31cefd54c75c37b540sberlin            assertTrue(contains);
5724e11457f34addf5d10fe0c31cefd54c75c37b540sberlin          } else {
5734e11457f34addf5d10fe0c31cefd54c75c37b540sberlin            otherMultibinders.add(visited);
5744e11457f34addf5d10fe0c31cefd54c75c37b540sberlin          }
5754e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        }
5764e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      }
5774faa20e3081448792933834aedfe972add806292Sam Berlin
5784faa20e3081448792933834aedfe972add806292Sam Berlin      if (collectionOfProvidersKey.equals(key)) {
5794faa20e3081448792933834aedfe972add806292Sam Berlin        assertTrue(contains);
5804faa20e3081448792933834aedfe972add806292Sam Berlin        assertFalse(matched);
5814faa20e3081448792933834aedfe972add806292Sam Berlin        collectionOfProvidersMatch = true;
582bed1413751f54dc6804dcb2a4c28300081788603flan      } else if (collectionOfJavaxProvidersKey.equals(key)) {
583bed1413751f54dc6804dcb2a4c28300081788603flan          assertTrue(contains);
584bed1413751f54dc6804dcb2a4c28300081788603flan          assertFalse(matched);
585bed1413751f54dc6804dcb2a4c28300081788603flan          collectionOfJavaxProvidersMatch = true;
5864faa20e3081448792933834aedfe972add806292Sam Berlin      } else if (!matched && contains) {
5874e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        otherContains.add(element);
5884e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      }
5894e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    }
5904faa20e3081448792933834aedfe972add806292Sam Berlin
5914e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    if(allowDuplicates) {
592c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin      assertEquals("wrong contained elements: " + otherContains,
593c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin          bindResults.size() + 1 + duplicates, otherContains.size());
5944e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    } else {
595c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin      assertEquals("wrong contained elements: " + otherContains,
596c34e0185fcf508a890c6cd13bdafeb505c3e9e8aSam Berlin          bindResults.size() + duplicates, otherContains.size());
5974e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    }
5984faa20e3081448792933834aedfe972add806292Sam Berlin
5994e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    assertEquals("other multibindings found: " + otherMultibinders, otherMultibindings,
6004e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        otherMultibinders.size());
6014faa20e3081448792933834aedfe972add806292Sam Berlin    assertTrue(collectionOfProvidersMatch);
602bed1413751f54dc6804dcb2a4c28300081788603flan    assertTrue(collectionOfJavaxProvidersMatch);
6034faa20e3081448792933834aedfe972add806292Sam Berlin
6044e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    // Validate that we can construct an injector out of the remaining bindings.
6054e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    Guice.createInjector(Elements.getModule(otherElements));
6064e11457f34addf5d10fe0c31cefd54c75c37b540sberlin  }
607af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber
608af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber  /**
609af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber   * Asserts that OptionalBinderBinding visitors for work correctly.
610af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber   *
611af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber   * @param <T> The type of the binding
612af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber   * @param keyType The key OptionalBinder is binding
613af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber   * @param modules The modules that define the bindings
614af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber   * @param visitType The kind of test we should perform. A live Injector, a raw Elements (Module)
615af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber   *        test, or both.
616af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber   * @param expectedOtherOptionalBindings the # of other optional bindings we expect to see.
617af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber   * @param expectedDefault the expected default binding, or null if none
618af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber   * @param expectedActual the expected actual binding, or null if none
619842f351c4b2b3b0a90d9f3bcf164d8fd19aede6cSam Berlin   * @param expectedUserLinkedActual the user binding that is the actual binding, used if
620842f351c4b2b3b0a90d9f3bcf164d8fd19aede6cSam Berlin   *        neither the default nor actual are set and a user binding existed for the type.
621af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber   */
622af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber  static <T> void assertOptionalVisitor(Key<T> keyType,
623af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      Iterable<? extends Module> modules,
624af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      VisitType visitType,
625af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      int expectedOtherOptionalBindings,
626af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      BindResult<?> expectedDefault,
627842f351c4b2b3b0a90d9f3bcf164d8fd19aede6cSam Berlin      BindResult<?> expectedActual,
628842f351c4b2b3b0a90d9f3bcf164d8fd19aede6cSam Berlin      BindResult<?> expectedUserLinkedActual) {
629af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    if (visitType == null) {
630af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      fail("must test something");
631af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    }
632af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber
6331d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb    // if java.util.Optional is bound, there'll be twice as many as we expect.
6341d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb    if (HAS_JAVA_OPTIONAL) {
6351d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb      expectedOtherOptionalBindings *= 2;
6361d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb    }
6371d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb
638af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    if (visitType == BOTH || visitType == INJECTOR) {
639af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      optionalInjectorTest(keyType, modules, expectedOtherOptionalBindings, expectedDefault,
640842f351c4b2b3b0a90d9f3bcf164d8fd19aede6cSam Berlin          expectedActual, expectedUserLinkedActual);
641af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    }
642af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber
643af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    if (visitType == BOTH || visitType == MODULE) {
644af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      optionalModuleTest(keyType, modules, expectedOtherOptionalBindings, expectedDefault,
645842f351c4b2b3b0a90d9f3bcf164d8fd19aede6cSam Berlin          expectedActual, expectedUserLinkedActual);
646af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    }
647af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber  }
648af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber
6491d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb  @SuppressWarnings({ "unchecked", "rawtypes" })
650842f351c4b2b3b0a90d9f3bcf164d8fd19aede6cSam Berlin  private static <T> void optionalInjectorTest(Key<T> keyType,
651842f351c4b2b3b0a90d9f3bcf164d8fd19aede6cSam Berlin      Iterable<? extends Module> modules,
652842f351c4b2b3b0a90d9f3bcf164d8fd19aede6cSam Berlin      int expectedOtherOptionalBindings,
653842f351c4b2b3b0a90d9f3bcf164d8fd19aede6cSam Berlin      BindResult<?> expectedDefault,
654842f351c4b2b3b0a90d9f3bcf164d8fd19aede6cSam Berlin      BindResult<?> expectedActual,
655842f351c4b2b3b0a90d9f3bcf164d8fd19aede6cSam Berlin      BindResult<?> expectedUserLinkedActual) {
656842f351c4b2b3b0a90d9f3bcf164d8fd19aede6cSam Berlin    if (expectedUserLinkedActual != null) {
657842f351c4b2b3b0a90d9f3bcf164d8fd19aede6cSam Berlin      assertNull("cannot have actual if expecting user binding", expectedActual);
658842f351c4b2b3b0a90d9f3bcf164d8fd19aede6cSam Berlin      assertNull("cannot have default if expecting user binding", expectedDefault);
659842f351c4b2b3b0a90d9f3bcf164d8fd19aede6cSam Berlin    }
6601d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb
661af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    Key<Optional<T>> optionalKey =
662af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        keyType.ofType(OptionalBinder.optionalOf(keyType.getTypeLiteral()));
6631d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb    Key<?> javaOptionalKey = HAS_JAVA_OPTIONAL ?
6641d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb        keyType.ofType(OptionalBinder.javaOptionalOf(keyType.getTypeLiteral())) : null;
665af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    Injector injector = Guice.createInjector(modules);
666af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    Binding<Optional<T>> optionalBinding = injector.getBinding(optionalKey);
6671d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb    Visitor visitor = new Visitor();
6681d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb    OptionalBinderBinding<Optional<T>> optionalBinder =
6691d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb        (OptionalBinderBinding<Optional<T>>) optionalBinding.acceptTargetVisitor(visitor);
670af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    assertNotNull(optionalBinder);
671af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    assertEquals(optionalKey, optionalBinder.getKey());
672af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber
6731d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb    Binding<?> javaOptionalBinding = null;
6741d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb    OptionalBinderBinding<?> javaOptionalBinder = null;
6751d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb    if (HAS_JAVA_OPTIONAL) {
6761d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb      javaOptionalBinding = injector.getBinding(javaOptionalKey);
6771d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb      javaOptionalBinder = (OptionalBinderBinding<?>) javaOptionalBinding.acceptTargetVisitor(visitor);
6781d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb      assertNotNull(javaOptionalBinder);
6791d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb      assertEquals(javaOptionalKey, javaOptionalBinder.getKey());
6801d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb    }
6811d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb
682af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    if (expectedDefault == null) {
683af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      assertNull("did not expect a default binding", optionalBinder.getDefaultBinding());
6841d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb      if (HAS_JAVA_OPTIONAL) {
6851d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb        assertNull("did not expect a default binding", javaOptionalBinder.getDefaultBinding());
6861d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb      }
687af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    } else {
688af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      assertTrue("expectedDefault: " + expectedDefault + ", actualDefault: "
6891d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb              + optionalBinder.getDefaultBinding(),
690af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber          matches(optionalBinder.getDefaultBinding(), expectedDefault));
6911d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb      if (HAS_JAVA_OPTIONAL) {
6921d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb        assertTrue("expectedDefault: " + expectedDefault + ", actualDefault: "
6931d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb                + javaOptionalBinder.getDefaultBinding(),
6941d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb            matches(javaOptionalBinder.getDefaultBinding(), expectedDefault));
6951d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb      }
696af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    }
697af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber
698842f351c4b2b3b0a90d9f3bcf164d8fd19aede6cSam Berlin    if (expectedActual == null && expectedUserLinkedActual == null) {
699af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      assertNull(optionalBinder.getActualBinding());
7001d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb      if (HAS_JAVA_OPTIONAL) {
7011d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb        assertNull(javaOptionalBinder.getActualBinding());
7021d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb      }
703842f351c4b2b3b0a90d9f3bcf164d8fd19aede6cSam Berlin    } else if (expectedActual != null) {
704af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      assertTrue("expectedActual: " + expectedActual + ", actualActual: "
7051d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb              + optionalBinder.getActualBinding(),
706af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber          matches(optionalBinder.getActualBinding(), expectedActual));
7071d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb      if (HAS_JAVA_OPTIONAL) {
7081d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb        assertTrue("expectedActual: " + expectedActual + ", actualActual: "
7091d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb                + javaOptionalBinder.getActualBinding(),
7101d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb            matches(javaOptionalBinder.getActualBinding(), expectedActual));
7111d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb      }
712842f351c4b2b3b0a90d9f3bcf164d8fd19aede6cSam Berlin    } else if (expectedUserLinkedActual != null) {
713842f351c4b2b3b0a90d9f3bcf164d8fd19aede6cSam Berlin      assertTrue("expectedUserLinkedActual: " + expectedUserLinkedActual + ", actualActual: "
7141d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb              + optionalBinder.getActualBinding(),
715842f351c4b2b3b0a90d9f3bcf164d8fd19aede6cSam Berlin          matches(optionalBinder.getActualBinding(), expectedUserLinkedActual));
7161d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb      if (HAS_JAVA_OPTIONAL) {
7171d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb        assertTrue("expectedUserLinkedActual: " + expectedUserLinkedActual + ", actualActual: "
7181d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb                + javaOptionalBinder.getActualBinding(),
7191d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb            matches(javaOptionalBinder.getActualBinding(), expectedUserLinkedActual));
7201d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb      }
721af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    }
722af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber
723af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber
724af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    Key<Optional<javax.inject.Provider<T>>> optionalJavaxProviderKey =
725af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        keyType.ofType(optionalOfJavaxProvider(keyType.getTypeLiteral()));
7261d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb    Key<?> javaOptionalJavaxProviderKey = HAS_JAVA_OPTIONAL ?
7271d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb        keyType.ofType(javaOptionalOfJavaxProvider(keyType.getTypeLiteral())) : null;
728af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    Key<Optional<Provider<T>>> optionalProviderKey =
729af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        keyType.ofType(optionalOfProvider(keyType.getTypeLiteral()));
7301d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb    Key<?> javaOptionalProviderKey = HAS_JAVA_OPTIONAL ?
7311d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb        keyType.ofType(javaOptionalOfProvider(keyType.getTypeLiteral())) : null;
732af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber
733af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    boolean keyMatch = false;
734af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    boolean optionalKeyMatch = false;
7351d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb    boolean javaOptionalKeyMatch = false;
736af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    boolean optionalJavaxProviderKeyMatch = false;
7371d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb    boolean javaOptionalJavaxProviderKeyMatch = false;
738af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    boolean optionalProviderKeyMatch = false;
7391d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb    boolean javaOptionalProviderKeyMatch = false;
740af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    boolean defaultMatch = false;
741af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    boolean actualMatch = false;
742af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    List<Object> otherOptionalBindings = Lists.newArrayList();
743af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    List<Binding> otherMatches = Lists.newArrayList();
744af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    for (Binding b : injector.getAllBindings().values()) {
745af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      boolean contains = optionalBinder.containsElement(b);
7461d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb      if (HAS_JAVA_OPTIONAL) {
7471d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb        assertEquals(contains, javaOptionalBinder.containsElement(b));
7481d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb      }
749af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      Object visited = b.acceptTargetVisitor(visitor);
750af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      if (visited instanceof OptionalBinderBinding) {
751af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        if (visited.equals(optionalBinder)) {
752af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber          assertTrue(contains);
7531d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb        } else if (HAS_JAVA_OPTIONAL && visited.equals(javaOptionalBinder)) {
7541d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb          assertTrue(contains);
755af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        } else {
756af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber          otherOptionalBindings.add(visited);
757af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        }
758af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      }
759af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      if (b.getKey().equals(keyType)) {
760af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        // keyType might match because a user bound it
761af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        // (which is possible in a purely absent OptionalBinder)
762af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        assertEquals(expectedDefault != null || expectedActual != null, contains);
763af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        if (contains) {
764af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber          keyMatch = true;
765af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        }
766af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      } else if (b.getKey().equals(optionalKey)) {
767af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        assertTrue(contains);
768af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        optionalKeyMatch = true;
7691d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb      } else if (b.getKey().equals(javaOptionalKey)) {
7701d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb        assertTrue(contains);
7711d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb        javaOptionalKeyMatch = true;
772af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      } else if (b.getKey().equals(optionalJavaxProviderKey)) {
773af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        assertTrue(contains);
774af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        optionalJavaxProviderKeyMatch = true;
7751d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb      } else if (b.getKey().equals(javaOptionalJavaxProviderKey)) {
7761d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb        assertTrue(contains);
7771d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb        javaOptionalJavaxProviderKeyMatch = true;
778af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      } else if (b.getKey().equals(optionalProviderKey)) {
779af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        assertTrue(contains);
780af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        optionalProviderKeyMatch = true;
7811d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb      } else if (b.getKey().equals(javaOptionalProviderKey)) {
7821d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb        assertTrue(contains);
7831d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb        javaOptionalProviderKeyMatch = true;
784af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      } else if (expectedDefault != null && matches(b, expectedDefault)) {
785af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        assertTrue(contains);
786af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        defaultMatch = true;
787af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      } else if (expectedActual != null && matches(b, expectedActual)) {
788af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        assertTrue(contains);
789af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        actualMatch = true;
790af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      } else if (contains) {
791af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        otherMatches.add(b);
792af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      }
793af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    }
794af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber
795af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    assertEquals(otherMatches.toString(), 0, otherMatches.size());
796af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    // only expect a keymatch if either default or actual are set
797af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    assertEquals(expectedDefault != null || expectedActual != null, keyMatch);
798af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    assertTrue(optionalKeyMatch);
799af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    assertTrue(optionalJavaxProviderKeyMatch);
800af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    assertTrue(optionalProviderKeyMatch);
8011d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb    assertEquals(HAS_JAVA_OPTIONAL, javaOptionalKeyMatch);
8021d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb    assertEquals(HAS_JAVA_OPTIONAL, javaOptionalJavaxProviderKeyMatch);
8031d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb    assertEquals(HAS_JAVA_OPTIONAL, javaOptionalProviderKeyMatch);
804af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    assertEquals(expectedDefault != null, defaultMatch);
805af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    assertEquals(expectedActual != null, actualMatch);
806af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    assertEquals("other OptionalBindings found: " + otherOptionalBindings,
807af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        expectedOtherOptionalBindings, otherOptionalBindings.size());
808af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber  }
809af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber
8101d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb  @SuppressWarnings({ "unchecked", "rawtypes" })
811842f351c4b2b3b0a90d9f3bcf164d8fd19aede6cSam Berlin  private static <T> void optionalModuleTest(Key<T> keyType,
812842f351c4b2b3b0a90d9f3bcf164d8fd19aede6cSam Berlin      Iterable<? extends Module> modules,
813842f351c4b2b3b0a90d9f3bcf164d8fd19aede6cSam Berlin      int expectedOtherOptionalBindings,
814842f351c4b2b3b0a90d9f3bcf164d8fd19aede6cSam Berlin      BindResult<?> expectedDefault,
815842f351c4b2b3b0a90d9f3bcf164d8fd19aede6cSam Berlin      BindResult<?> expectedActual,
816842f351c4b2b3b0a90d9f3bcf164d8fd19aede6cSam Berlin      BindResult<?> expectedUserLinkedActual) {
817842f351c4b2b3b0a90d9f3bcf164d8fd19aede6cSam Berlin    if (expectedUserLinkedActual != null) {
818842f351c4b2b3b0a90d9f3bcf164d8fd19aede6cSam Berlin      assertNull("cannot have actual if expecting user binding", expectedActual);
819842f351c4b2b3b0a90d9f3bcf164d8fd19aede6cSam Berlin      assertNull("cannot have default if expecting user binding", expectedDefault);
820842f351c4b2b3b0a90d9f3bcf164d8fd19aede6cSam Berlin    }
821af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    Set<Element> elements = ImmutableSet.copyOf(Elements.getElements(modules));
822af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    Map<Key<?>, Binding<?>> indexed = index(elements);
823af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    Key<Optional<T>> optionalKey =
824af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        keyType.ofType(OptionalBinder.optionalOf(keyType.getTypeLiteral()));
8251d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb    Key<?> javaOptionalKey = HAS_JAVA_OPTIONAL ?
8261d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb        keyType.ofType(OptionalBinder.javaOptionalOf(keyType.getTypeLiteral())) : null;
8271d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb    Visitor visitor = new Visitor();
8281d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb    OptionalBinderBinding<Optional<T>> optionalBinder = null;
8291d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb    OptionalBinderBinding<?> javaOptionalBinder = null;
830af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    Key<?> defaultKey = null;
831af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    Key<?> actualKey = null;
832af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber
833af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    Binding optionalBinding = indexed.get(optionalKey);
8341d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb    optionalBinder =
8351d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb        (OptionalBinderBinding<Optional<T>>) optionalBinding.acceptTargetVisitor(visitor);
8361d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb
8371d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb    if (HAS_JAVA_OPTIONAL) {
8381d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb      Binding javaOptionalBinding = indexed.get(javaOptionalKey);
8391d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb      javaOptionalBinder = (OptionalBinderBinding) javaOptionalBinding.acceptTargetVisitor(visitor);
8401d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb    }
841af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber
842af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    // Locate the defaultKey & actualKey
843af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    for (Element element : elements) {
844af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      if (optionalBinder.containsElement(element) && element instanceof Binding) {
845af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        Binding binding = (Binding) element;
846af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        if (isSourceEntry(binding, Source.DEFAULT)) {
847c66f08e3d6798e88f35be51679854568f337e7ebSam Berlin          defaultKey = binding.getKey();
848af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        } else if (isSourceEntry(binding, Source.ACTUAL)) {
849c66f08e3d6798e88f35be51679854568f337e7ebSam Berlin          actualKey = binding.getKey();
850af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        }
851af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      }
852af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    }
853af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    assertNotNull(optionalBinder);
8541d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb    if (HAS_JAVA_OPTIONAL) {
8551d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb      assertNotNull(javaOptionalBinder);
8561d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb    }
857af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    assertEquals(expectedDefault == null, defaultKey == null);
858af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    assertEquals(expectedActual == null, actualKey == null);
859af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber
860af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    Key<Optional<javax.inject.Provider<T>>> optionalJavaxProviderKey =
861af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        keyType.ofType(optionalOfJavaxProvider(keyType.getTypeLiteral()));
8621d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb    Key<?> javaOptionalJavaxProviderKey = HAS_JAVA_OPTIONAL ?
8631d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb        keyType.ofType(javaOptionalOfJavaxProvider(keyType.getTypeLiteral())) : null;
864af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    Key<Optional<Provider<T>>> optionalProviderKey =
865af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        keyType.ofType(optionalOfProvider(keyType.getTypeLiteral()));
8661d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb    Key<?> javaOptionalProviderKey = HAS_JAVA_OPTIONAL ?
8671d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb        keyType.ofType(javaOptionalOfProvider(keyType.getTypeLiteral())) : null;
868af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    boolean keyMatch = false;
869af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    boolean optionalKeyMatch = false;
8701d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb    boolean javaOptionalKeyMatch = false;
871af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    boolean optionalJavaxProviderKeyMatch = false;
8721d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb    boolean javaOptionalJavaxProviderKeyMatch = false;
873af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    boolean optionalProviderKeyMatch = false;
8741d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb    boolean javaOptionalProviderKeyMatch = false;
875af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    boolean defaultMatch = false;
876af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    boolean actualMatch = false;
877af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    List<Object> otherOptionalElements = Lists.newArrayList();
878af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    List<Element> otherContains = Lists.newArrayList();
879af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    List<Element> nonContainedElements = Lists.newArrayList();
880af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    for (Element element : elements) {
881af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      boolean contains = optionalBinder.containsElement(element);
8821d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb      if (HAS_JAVA_OPTIONAL) {
8831d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb        assertEquals(contains, javaOptionalBinder.containsElement(element));
8841d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb      }
885af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      if (!contains) {
886af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        nonContainedElements.add(element);
887af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      }
888af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      Key key = null;
889af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      Binding b = null;
890af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      if (element instanceof Binding) {
891af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        b = (Binding) element;
892af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        key = b.getKey();
893af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        Object visited = b.acceptTargetVisitor(visitor);
894af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        if (visited instanceof OptionalBinderBinding) {
895af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber          if (visited.equals(optionalBinder)) {
896af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber            assertTrue(contains);
8971d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb          } else if (HAS_JAVA_OPTIONAL && visited.equals(javaOptionalBinder)) {
8981d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb            assertTrue(contains);
899af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber          } else {
900af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber            otherOptionalElements.add(visited);
901af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber          }
902af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        }
903af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      } else if (element instanceof ProviderLookup) {
904af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        key = ((ProviderLookup) element).getKey();
905af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      }
906af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber
907af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      if (key != null && key.equals(keyType)) {
908af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        // keyType might match because a user bound it
909af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        // (which is possible in a purely absent OptionalBinder)
910af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        assertEquals(expectedDefault != null || expectedActual != null, contains);
911af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        if (contains) {
912af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber          keyMatch = true;
913af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        }
914af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      } else if (key != null && key.equals(optionalKey)) {
915af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        assertTrue(contains);
916af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        optionalKeyMatch = true;
9171d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb      } else if (key != null && key.equals(javaOptionalKey)) {
9181d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb        assertTrue(contains);
9191d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb        javaOptionalKeyMatch = true;
920af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      } else if (key != null && key.equals(optionalJavaxProviderKey)) {
921af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        assertTrue(contains);
922af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        optionalJavaxProviderKeyMatch = true;
9231d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb      } else if (key != null && key.equals(javaOptionalJavaxProviderKey)) {
9241d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb        assertTrue(contains);
9251d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb        javaOptionalJavaxProviderKeyMatch = true;
926af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      } else if (key != null && key.equals(optionalProviderKey)) {
927af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        assertTrue(contains);
928af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        optionalProviderKeyMatch = true;
9291d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb      } else if (key != null && key.equals(javaOptionalProviderKey)) {
9301d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb        assertTrue(contains);
9311d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb        javaOptionalProviderKeyMatch = true;
932af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      } else if (key != null && key.equals(defaultKey)) {
933af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        assertTrue(contains);
934af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        if (b != null) { // otherwise it might just be a ProviderLookup into it
935af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber          assertTrue("expected: " + expectedDefault + ", but was: " + b,
936af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber              matches(b, expectedDefault));
937af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber          defaultMatch = true;
938af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        }
939af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      } else if (key != null && key.equals(actualKey)) {
940af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        assertTrue(contains);
941af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        if (b != null) { // otherwise it might just be a ProviderLookup into it
942af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber          assertTrue("expected: " + expectedActual + ", but was: " + b, matches(b, expectedActual));
943af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber          actualMatch = true;
944af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        }
945af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      } else if (contains) {
946af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        otherContains.add(element);
947af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      }
948af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    }
949af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber
950af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    // only expect a keymatch if either default or actual are set
951af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    assertEquals(expectedDefault != null || expectedActual != null, keyMatch);
952af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    assertTrue(optionalKeyMatch);
953af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    assertTrue(optionalJavaxProviderKeyMatch);
954af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    assertTrue(optionalProviderKeyMatch);
9551d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb    assertEquals(HAS_JAVA_OPTIONAL, javaOptionalKeyMatch);
9561d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb    assertEquals(HAS_JAVA_OPTIONAL, javaOptionalJavaxProviderKeyMatch);
9571d3f8cd0a163c7439589d80e6377edbf2ca4b43dsameb    assertEquals(HAS_JAVA_OPTIONAL, javaOptionalProviderKeyMatch);
958af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    assertEquals(expectedDefault != null, defaultMatch);
959af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    assertEquals(expectedActual != null, actualMatch);
960af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    assertEquals(otherContains.toString(), 0, otherContains.size());
961af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    assertEquals("other OptionalBindings found: " + otherOptionalElements,
962af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        expectedOtherOptionalBindings, otherOptionalElements.size());
963af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber
964af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber     // Validate that we can construct an injector out of the remaining bindings.
965af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    Guice.createInjector(Elements.getModule(nonContainedElements));
966af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber  }
967af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber
968af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber  private static boolean isSourceEntry(Binding b, Source type) {
969c66f08e3d6798e88f35be51679854568f337e7ebSam Berlin    switch(type) {
970c66f08e3d6798e88f35be51679854568f337e7ebSam Berlin      case ACTUAL:
971c66f08e3d6798e88f35be51679854568f337e7ebSam Berlin        return b.getKey().getAnnotation() instanceof OptionalBinder.Actual;
972c66f08e3d6798e88f35be51679854568f337e7ebSam Berlin      case DEFAULT:
973c66f08e3d6798e88f35be51679854568f337e7ebSam Berlin        return b.getKey().getAnnotation() instanceof OptionalBinder.Default;
974c66f08e3d6798e88f35be51679854568f337e7ebSam Berlin      default:
975c66f08e3d6798e88f35be51679854568f337e7ebSam Berlin        throw new IllegalStateException("invalid type: " + type);
976af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    }
977af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber  }
978af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber
979af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber  /** Returns the subset of elements that have keys, indexed by them. */
980af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber  private static Map<Key<?>, Binding<?>> index(Iterable<Element> elements) {
981af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    ImmutableMap.Builder<Key<?>, Binding<?>> builder = ImmutableMap.builder();
982af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    for (Element element : elements) {
983af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      if (element instanceof Binding) {
984af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        builder.put(((Binding) element).getKey(), (Binding) element);
985af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      }
986af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    }
987af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    return builder.build();
988af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber  }
9894e11457f34addf5d10fe0c31cefd54c75c37b540sberlin
9904e11457f34addf5d10fe0c31cefd54c75c37b540sberlin  static <K, V> MapResult instance(K k, V v) {
9914e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    return new MapResult<K, V>(k, new BindResult<V>(INSTANCE, v, null));
9924e11457f34addf5d10fe0c31cefd54c75c37b540sberlin  }
9934e11457f34addf5d10fe0c31cefd54c75c37b540sberlin
9944e11457f34addf5d10fe0c31cefd54c75c37b540sberlin  static <K, V> MapResult linked(K k, Class<? extends V> clazz) {
9954e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    return new MapResult<K, V>(k, new BindResult<V>(LINKED, null, Key.get(clazz)));
9964e11457f34addf5d10fe0c31cefd54c75c37b540sberlin  }
9974e11457f34addf5d10fe0c31cefd54c75c37b540sberlin
9984e11457f34addf5d10fe0c31cefd54c75c37b540sberlin  static <K, V> MapResult linked(K k, Key<? extends V> key) {
9994e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    return new MapResult<K, V>(k, new BindResult<V>(LINKED, null, key));
10004e11457f34addf5d10fe0c31cefd54c75c37b540sberlin  }
10014e11457f34addf5d10fe0c31cefd54c75c37b540sberlin
10024e11457f34addf5d10fe0c31cefd54c75c37b540sberlin  static <K, V> MapResult providerInstance(K k, V v) {
10034e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    return new MapResult<K, V>(k, new BindResult<V>(PROVIDER_INSTANCE, v, null));
10044e11457f34addf5d10fe0c31cefd54c75c37b540sberlin  }
10054e11457f34addf5d10fe0c31cefd54c75c37b540sberlin
1006bf2b16c06a5ff7c099fe60f9a46cfb130ce45962Christian Edward Gruber  static class MapResult<K, V> {
10074e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    private final K k;
10084e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    private final BindResult<V> v;
10094e11457f34addf5d10fe0c31cefd54c75c37b540sberlin
10104e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    MapResult(K k, BindResult<V> v) {
10114e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      this.k = k;
10124e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      this.v = v;
10134e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    }
10144e11457f34addf5d10fe0c31cefd54c75c37b540sberlin
10154e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    @Override
10164e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    public String toString() {
10174e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      return "entry[key[" + k + "],value[" + v + "]]";
10184e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    }
10194e11457f34addf5d10fe0c31cefd54c75c37b540sberlin  }
1020af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber
1021af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber  private static boolean matches(Binding<?> item, BindResult<?> result) {
1022af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    switch (result.type) {
1023af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    case INSTANCE:
1024af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      if (item instanceof InstanceBinding
1025af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber          && ((InstanceBinding) item).getInstance().equals(result.instance)) {
1026af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        return true;
1027af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      }
1028af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      break;
1029af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    case LINKED:
1030af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      if (item instanceof LinkedKeyBinding
1031af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber          && ((LinkedKeyBinding) item).getLinkedKey().equals(result.key)) {
1032af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        return true;
1033af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      }
1034af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      break;
1035af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    case PROVIDER_INSTANCE:
1036af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      if (item instanceof ProviderInstanceBinding
1037af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber          && Objects.equal(((ProviderInstanceBinding) item).getUserSuppliedProvider().get(),
1038af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber                           result.instance)) {
1039af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        return true;
1040af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      }
1041af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      break;
1042af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    case PROVIDER_KEY:
1043af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      if (item instanceof ProviderKeyBinding
1044af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber          && ((ProviderKeyBinding) item).getProviderKey().equals(result.key)) {
1045af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        return true;
1046af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      }
1047af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      break;
1048af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    }
1049af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    return false;
1050af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber  }
10514e11457f34addf5d10fe0c31cefd54c75c37b540sberlin
1052af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber  static <T> BindResult<T> instance(T t) {
10534e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    return new BindResult<T>(INSTANCE, t, null);
10544e11457f34addf5d10fe0c31cefd54c75c37b540sberlin  }
10554e11457f34addf5d10fe0c31cefd54c75c37b540sberlin
1056af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber  static <T> BindResult<T> linked(Class<? extends T> clazz) {
10574e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    return new BindResult<T>(LINKED, null, Key.get(clazz));
10584e11457f34addf5d10fe0c31cefd54c75c37b540sberlin  }
10594e11457f34addf5d10fe0c31cefd54c75c37b540sberlin
1060af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber  static <T> BindResult<T> linked(Key<? extends T> key) {
10614e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    return new BindResult<T>(LINKED, null, key);
10624e11457f34addf5d10fe0c31cefd54c75c37b540sberlin  }
10634e11457f34addf5d10fe0c31cefd54c75c37b540sberlin
1064af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber  static <T> BindResult<T> providerInstance(T t) {
10654e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    return new BindResult<T>(PROVIDER_INSTANCE, t, null);
10664e11457f34addf5d10fe0c31cefd54c75c37b540sberlin  }
1067af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber
1068af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber  static <T> BindResult<T> providerKey(Key<T> key) {
1069af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    return new BindResult<T>(PROVIDER_KEY, null, key);
1070af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber  }
10714e11457f34addf5d10fe0c31cefd54c75c37b540sberlin
10724e11457f34addf5d10fe0c31cefd54c75c37b540sberlin  /** The kind of binding. */
1073af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber  static enum BindType { INSTANCE, LINKED, PROVIDER_INSTANCE, PROVIDER_KEY }
10744e11457f34addf5d10fe0c31cefd54c75c37b540sberlin  /** The result of the binding. */
1075bf2b16c06a5ff7c099fe60f9a46cfb130ce45962Christian Edward Gruber  static class BindResult<T> {
10764e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    private final BindType type;
1077af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    private final Key<?> key;
10784e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    private final T instance;
10794e11457f34addf5d10fe0c31cefd54c75c37b540sberlin
1080af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    private BindResult(BindType type, T instance, Key<?> key) {
10814e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      this.type = type;
10824e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      this.instance = instance;
10834e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      this.key = key;
10844e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    }
10854e11457f34addf5d10fe0c31cefd54c75c37b540sberlin
10864e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    @Override
10874e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    public String toString() {
10884e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      switch(type) {
10894e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      case INSTANCE:
10904e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        return "instance[" + instance + "]";
10914e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      case LINKED:
10924e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        return "linkedKey[" + key + "]";
10934e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      case PROVIDER_INSTANCE:
10944e11457f34addf5d10fe0c31cefd54c75c37b540sberlin        return "providerInstance[" + instance + "]";
1095af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      case PROVIDER_KEY:
1096af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber        return "providerKey[" + key + "]";
10974e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      }
10984e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      return null;
10994e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    }
11004e11457f34addf5d10fe0c31cefd54c75c37b540sberlin  }
11014e11457f34addf5d10fe0c31cefd54c75c37b540sberlin
11024e11457f34addf5d10fe0c31cefd54c75c37b540sberlin  private static class Visitor<T> extends
11034e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      DefaultBindingTargetVisitor<T, Object> implements MultibindingsTargetVisitor<T, Object> {
11044e11457f34addf5d10fe0c31cefd54c75c37b540sberlin
11054e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    public Object visit(MultibinderBinding<? extends T> multibinding) {
11064e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      return multibinding;
11074e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    }
11084e11457f34addf5d10fe0c31cefd54c75c37b540sberlin
11094e11457f34addf5d10fe0c31cefd54c75c37b540sberlin    public Object visit(MapBinderBinding<? extends T> mapbinding) {
11104e11457f34addf5d10fe0c31cefd54c75c37b540sberlin      return mapbinding;
1111af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    }
1112af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber
1113af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    public Object visit(OptionalBinderBinding<? extends T> optionalbinding) {
1114af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber      return optionalbinding;
1115af24f632f90fc91daeb30172b874405c131592c8Christian Edward Gruber    }
11164e11457f34addf5d10fe0c31cefd54c75c37b540sberlin  }
11174e11457f34addf5d10fe0c31cefd54c75c37b540sberlin}
1118