public void testConstrainedBiMapLegal() {
   BiMap<String, Integer> map =
       new AbstractBiMap<String, Integer>(
           Maps.<String, Integer>newLinkedHashMap(), Maps.<Integer, String>newLinkedHashMap()) {};
   BiMap<String, Integer> constrained = MapConstraints.constrainedBiMap(map, TEST_CONSTRAINT);
   map.put(TEST_KEY, TEST_VALUE);
   constrained.put("foo", 1);
   map.putAll(ImmutableMap.of("bar", 2));
   constrained.putAll(ImmutableMap.of("baz", 3));
   assertTrue(map.equals(constrained));
   assertTrue(constrained.equals(map));
   assertEquals(map.entrySet(), constrained.entrySet());
   assertEquals(map.keySet(), constrained.keySet());
   assertEquals(map.values(), constrained.values());
   assertEquals(map.toString(), constrained.toString());
   assertEquals(map.hashCode(), constrained.hashCode());
   ASSERT
       .that(map.entrySet())
       .has()
       .allOf(
           Maps.immutableEntry(TEST_KEY, TEST_VALUE),
           Maps.immutableEntry("foo", 1),
           Maps.immutableEntry("bar", 2),
           Maps.immutableEntry("baz", 3))
       .inOrder();
 }
 @Override
 protected void assertMoreInvariants(Map<String, Integer> map) {
   BiMap<String, Integer> bimap = (BiMap<String, Integer>) map;
   BiMap<Integer, String> inverse = bimap.inverse();
   assertEquals(bimap.size(), inverse.size());
   for (Entry<String, Integer> entry : bimap.entrySet()) {
     assertEquals(entry.getKey(), inverse.get(entry.getValue()));
   }
   for (Entry<Integer, String> entry : inverse.entrySet()) {
     assertEquals(entry.getKey(), bimap.get(entry.getValue()));
   }
 }
 public void testConstrainedBiMapIllegal() {
   BiMap<String, Integer> map =
       new AbstractBiMap<String, Integer>(
           Maps.<String, Integer>newLinkedHashMap(), Maps.<Integer, String>newLinkedHashMap()) {};
   BiMap<String, Integer> constrained = MapConstraints.constrainedBiMap(map, TEST_CONSTRAINT);
   try {
     constrained.put(TEST_KEY, TEST_VALUE);
     fail("TestKeyException expected");
   } catch (TestKeyException expected) {
   }
   try {
     constrained.put("baz", TEST_VALUE);
     fail("TestValueException expected");
   } catch (TestValueException expected) {
   }
   try {
     constrained.put(TEST_KEY, 3);
     fail("TestKeyException expected");
   } catch (TestKeyException expected) {
   }
   try {
     constrained.putAll(ImmutableMap.of("baz", 3, TEST_KEY, 4));
     fail("TestKeyException expected");
   } catch (TestKeyException expected) {
   }
   try {
     constrained.forcePut(TEST_KEY, 3);
     fail("TestKeyException expected");
   } catch (TestKeyException expected) {
   }
   try {
     constrained.inverse().forcePut(TEST_VALUE, "baz");
     fail("TestValueException expected");
   } catch (TestValueException expected) {
   }
   try {
     constrained.inverse().forcePut(3, TEST_KEY);
     fail("TestKeyException expected");
   } catch (TestKeyException expected) {
   }
   assertEquals(Collections.emptySet(), map.entrySet());
   assertEquals(Collections.emptySet(), constrained.entrySet());
 }
  public String toString() {
    StringBuilder sb = new StringBuilder();
    lock.readLock().lock();
    try {
      Set<InetAddress> eps = tokenToEndPointMap.inverse().keySet();

      if (!eps.isEmpty()) {
        sb.append("Normal Tokens:");
        sb.append(System.getProperty("line.separator"));
        for (InetAddress ep : eps) {
          sb.append(ep);
          sb.append(":");
          sb.append(tokenToEndPointMap.inverse().get(ep));
          sb.append(System.getProperty("line.separator"));
        }
      }

      if (!bootstrapTokens.isEmpty()) {
        sb.append("Bootstrapping Tokens:");
        sb.append(System.getProperty("line.separator"));
        for (Map.Entry<Token, InetAddress> entry : bootstrapTokens.entrySet()) {
          sb.append(entry.getValue() + ":" + entry.getKey());
          sb.append(System.getProperty("line.separator"));
        }
      }

      if (!leavingEndPoints.isEmpty()) {
        sb.append("Leaving EndPoints:");
        sb.append(System.getProperty("line.separator"));
        for (InetAddress ep : leavingEndPoints) {
          sb.append(ep);
          sb.append(System.getProperty("line.separator"));
        }
      }

      if (!pendingRanges.isEmpty()) {
        sb.append("Pending Ranges:");
        sb.append(System.getProperty("line.separator"));
        sb.append(printPendingRanges());
      }
    } finally {
      lock.readLock().unlock();
    }

    return sb.toString();
  }