View Javadoc
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")
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  
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  
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  
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
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 }