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
15package com.google.common.collect;
16
17import static com.google.common.base.Preconditions.checkArgument;
18
19import com.google.common.annotations.GwtCompatible;
20import com.google.common.annotations.GwtIncompatible;
21import com.google.common.collect.testing.features.CollectionFeature;
22import com.google.common.collect.testing.features.CollectionSize;
23import com.google.common.collect.testing.google.MultisetTestSuiteBuilder;
24import com.google.common.collect.testing.google.TestStringMultisetGenerator;
25
26import junit.framework.Test;
27import junit.framework.TestCase;
28import junit.framework.TestSuite;
29
30import java.io.Serializable;
31import java.util.Collections;
32import java.util.Iterator;
33import java.util.Map;
34import java.util.concurrent.atomic.AtomicInteger;
35
36import 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)
46public 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}
143