1/*
2 * Copyright (C) 2007 The Guava Authors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.google.common.collect;
18
19import static com.google.common.collect.Multisets.setCountImpl;
20
21import com.google.common.annotations.GwtCompatible;
22import com.google.common.base.Objects;
23
24import java.util.AbstractCollection;
25import java.util.Collection;
26import java.util.Iterator;
27import java.util.Set;
28
29import javax.annotation.Nullable;
30
31/**
32 * This class provides a skeletal implementation of the {@link Multiset}
33 * interface. A new multiset implementation can be created easily by extending
34 * this class and implementing the {@link Multiset#entrySet()} method, plus
35 * optionally overriding {@link #add(Object, int)} and
36 * {@link #remove(Object, int)} to enable modifications to the multiset.
37 *
38 * <p>The {@link #count} and {@link #size} implementations all iterate across
39 * the set returned by {@link Multiset#entrySet()}, as do many methods acting on
40 * the set returned by {@link #elementSet()}. Override those methods for better
41 * performance.
42 *
43 * @author Kevin Bourrillion
44 * @author Louis Wasserman
45 */
46@GwtCompatible
47abstract class AbstractMultiset<E> extends AbstractCollection<E>
48    implements Multiset<E> {
49  // Query Operations
50
51  @Override public int size() {
52    return Multisets.sizeImpl(this);
53  }
54
55  @Override public boolean isEmpty() {
56    return entrySet().isEmpty();
57  }
58
59  @Override public boolean contains(@Nullable Object element) {
60    return count(element) > 0;
61  }
62
63  @Override public Iterator<E> iterator() {
64    return Multisets.iteratorImpl(this);
65  }
66
67  @Override
68  public int count(Object element) {
69    for (Entry<E> entry : entrySet()) {
70      if (Objects.equal(entry.getElement(), element)) {
71        return entry.getCount();
72      }
73    }
74    return 0;
75  }
76
77  // Modification Operations
78
79  @Override public boolean add(@Nullable E element) {
80    add(element, 1);
81    return true;
82  }
83
84  @Override
85  public int add(E element, int occurrences) {
86    throw new UnsupportedOperationException();
87  }
88
89  @Override public boolean remove(Object element) {
90    return remove(element, 1) > 0;
91  }
92
93  @Override
94  public int remove(Object element, int occurrences) {
95    throw new UnsupportedOperationException();
96  }
97
98  @Override
99  public int setCount(E element, int count) {
100    return setCountImpl(this, element, count);
101  }
102
103  @Override
104  public boolean setCount(E element, int oldCount, int newCount) {
105    return setCountImpl(this, element, oldCount, newCount);
106  }
107
108  // Bulk Operations
109
110  /**
111   * {@inheritDoc}
112   *
113   * <p>This implementation is highly efficient when {@code elementsToAdd}
114   * is itself a {@link Multiset}.
115   */
116  @Override public boolean addAll(Collection<? extends E> elementsToAdd) {
117    return Multisets.addAllImpl(this, elementsToAdd);
118  }
119
120  @Override public boolean removeAll(Collection<?> elementsToRemove) {
121    return Multisets.removeAllImpl(this, elementsToRemove);
122  }
123
124  @Override public boolean retainAll(Collection<?> elementsToRetain) {
125    return Multisets.retainAllImpl(this, elementsToRetain);
126  }
127
128  @Override public void clear() {
129    Iterators.clear(entryIterator());
130  }
131
132  // Views
133
134  private transient Set<E> elementSet;
135
136  @Override
137  public Set<E> elementSet() {
138    Set<E> result = elementSet;
139    if (result == null) {
140      elementSet = result = createElementSet();
141    }
142    return result;
143  }
144
145  /**
146   * Creates a new instance of this multiset's element set, which will be
147   * returned by {@link #elementSet()}.
148   */
149  Set<E> createElementSet() {
150    return new ElementSet();
151  }
152
153  class ElementSet extends Multisets.ElementSet<E> {
154    @Override
155    Multiset<E> multiset() {
156      return AbstractMultiset.this;
157    }
158  }
159
160  abstract Iterator<Entry<E>> entryIterator();
161
162  abstract int distinctElements();
163
164  private transient Set<Entry<E>> entrySet;
165
166  @Override public Set<Entry<E>> entrySet() {
167    Set<Entry<E>> result = entrySet;
168    return (result == null) ? entrySet = createEntrySet() : result;
169  }
170
171  class EntrySet extends Multisets.EntrySet<E> {
172    @Override Multiset<E> multiset() {
173      return AbstractMultiset.this;
174    }
175
176    @Override public Iterator<Entry<E>> iterator() {
177      return entryIterator();
178    }
179
180    @Override public int size() {
181      return distinctElements();
182    }
183  }
184
185  Set<Entry<E>> createEntrySet() {
186    return new EntrySet();
187  }
188
189  // Object methods
190
191  /**
192   * {@inheritDoc}
193   *
194   * <p>This implementation returns {@code true} if {@code object} is a multiset
195   * of the same size and if, for each element, the two multisets have the same
196   * count.
197   */
198  @Override public boolean equals(@Nullable Object object) {
199    return Multisets.equalsImpl(this, object);
200  }
201
202  /**
203   * {@inheritDoc}
204   *
205   * <p>This implementation returns the hash code of {@link
206   * Multiset#entrySet()}.
207   */
208  @Override public int hashCode() {
209    return entrySet().hashCode();
210  }
211
212  /**
213   * {@inheritDoc}
214   *
215   * <p>This implementation returns the result of invoking {@code toString} on
216   * {@link Multiset#entrySet()}.
217   */
218  @Override public String toString() {
219    return entrySet().toString();
220  }
221}
222