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