1 /* 2 * Copyright (C) 2007 The Guava Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 * in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the License 10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 * or implied. See the License for the specific language governing permissions and limitations under 12 * the License. 13 */ 14 15 package com.google.common.collect; 16 17 import static com.google.common.base.Preconditions.checkArgument; 18 19 import com.google.common.annotations.GwtCompatible; 20 import com.google.common.annotations.GwtIncompatible; 21 import com.google.common.collect.testing.features.CollectionFeature; 22 import com.google.common.collect.testing.features.CollectionSize; 23 import com.google.common.collect.testing.google.MultisetTestSuiteBuilder; 24 import com.google.common.collect.testing.google.TestStringMultisetGenerator; 25 26 import junit.framework.Test; 27 import junit.framework.TestCase; 28 import junit.framework.TestSuite; 29 30 import java.io.Serializable; 31 import java.util.Collections; 32 import java.util.Iterator; 33 import java.util.Map; 34 import java.util.concurrent.atomic.AtomicInteger; 35 36 import javax.annotation.Nullable; 37 38 /** 39 * Unit test for {@link AbstractMultiset}. 40 * 41 * @author Kevin Bourrillion 42 * @author Louis Wasserman 43 */ 44 @SuppressWarnings("serial") // No serialization is used in this test 45 @GwtCompatible(emulated = true) 46 public class SimpleAbstractMultisetTest extends TestCase { 47 @GwtIncompatible("suite") suite()48 public static Test suite() { 49 TestSuite suite = new TestSuite(); 50 suite.addTestSuite(SimpleAbstractMultisetTest.class); 51 suite.addTest(MultisetTestSuiteBuilder.using(new TestStringMultisetGenerator() { 52 @Override 53 protected Multiset<String> create(String[] elements) { 54 Multiset<String> ms = new NoRemoveMultiset<String>(); 55 Collections.addAll(ms, elements); 56 return ms; 57 } 58 }) 59 .named("NoRemoveMultiset") 60 .withFeatures(CollectionSize.ANY, CollectionFeature.ALLOWS_NULL_VALUES, 61 CollectionFeature.SUPPORTS_ADD) 62 .createTestSuite()); 63 return suite; 64 } 65 testFastAddAllMultiset()66 public void testFastAddAllMultiset() { 67 final AtomicInteger addCalls = new AtomicInteger(); 68 Multiset<String> multiset = new NoRemoveMultiset<String>() { 69 @Override 70 public int add(String element, int occurrences) { 71 addCalls.incrementAndGet(); 72 return super.add(element, occurrences); 73 } 74 }; 75 ImmutableMultiset<String> adds = 76 new ImmutableMultiset.Builder<String>().addCopies("x", 10).build(); 77 multiset.addAll(adds); 78 assertEquals(addCalls.get(), 1); 79 } 80 testRemoveUnsupported()81 public void testRemoveUnsupported() { 82 Multiset<String> multiset = new NoRemoveMultiset<String>(); 83 multiset.add("a"); 84 try { 85 multiset.remove("a"); 86 fail(); 87 } catch (UnsupportedOperationException expected) {} 88 assertTrue(multiset.contains("a")); 89 } 90 91 private static class NoRemoveMultiset<E> extends AbstractMultiset<E> 92 implements Serializable { 93 final Map<E, Integer> backingMap = Maps.newHashMap(); 94 add(@ullable E element, int occurrences)95 @Override public int add(@Nullable E element, int occurrences) { 96 checkArgument(occurrences >= 0); 97 Integer frequency = backingMap.get(element); 98 if (frequency == null) { 99 frequency = 0; 100 } 101 if (occurrences == 0) { 102 return frequency; 103 } 104 checkArgument(occurrences <= Integer.MAX_VALUE - frequency); 105 backingMap.put(element, frequency + occurrences); 106 return frequency; 107 } 108 109 @Override entryIterator()110 Iterator<Entry<E>> entryIterator() { 111 final Iterator<Map.Entry<E, Integer>> backingEntries = backingMap.entrySet().iterator(); 112 return new UnmodifiableIterator<Multiset.Entry<E>>() { 113 @Override 114 public boolean hasNext() { 115 return backingEntries.hasNext(); 116 } 117 118 @Override 119 public Multiset.Entry<E> next() { 120 final Map.Entry<E, Integer> mapEntry = backingEntries.next(); 121 return new Multisets.AbstractEntry<E>() { 122 @Override 123 public E getElement() { 124 return mapEntry.getKey(); 125 } 126 127 @Override 128 public int getCount() { 129 Integer frequency = backingMap.get(getElement()); 130 return (frequency == null) ? 0 : frequency; 131 } 132 }; 133 } 134 }; 135 } 136 137 @Override 138 int distinctElements() { 139 return backingMap.size(); 140 } 141 } 142 } 143