001    /*
002     * Copyright (C) 2007 Google Inc.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    package com.google.common.collect;
018    
019    import static com.google.common.base.Preconditions.checkArgument;
020    import static com.google.common.base.Preconditions.checkNotNull;
021    
022    import com.google.common.annotations.GwtCompatible;
023    import com.google.common.base.Objects;
024    
025    import java.io.Serializable;
026    import java.util.AbstractSet;
027    import java.util.Collection;
028    import java.util.Collections;
029    import java.util.Iterator;
030    import java.util.Set;
031    
032    import javax.annotation.Nullable;
033    
034    /**
035     * Provides static utility methods for creating and working with {@link
036     * Multiset} instances.
037     *
038     * @author Kevin Bourrillion
039     * @author Mike Bostock
040     * @since 2 (imported from Google Collections Library)
041     */
042    @GwtCompatible
043    public final class Multisets {
044      private Multisets() {}
045    
046      /**
047       * Returns an unmodifiable view of the specified multiset. Query operations on
048       * the returned multiset "read through" to the specified multiset, and
049       * attempts to modify the returned multiset result in an
050       * {@link UnsupportedOperationException}.
051       *
052       * <p>The returned multiset will be serializable if the specified multiset is
053       * serializable.
054       *
055       * @param multiset the multiset for which an unmodifiable view is to be
056       *     generated
057       * @return an unmodifiable view of the multiset
058       */
059      public static <E> Multiset<E> unmodifiableMultiset(
060          Multiset<? extends E> multiset) {
061        return new UnmodifiableMultiset<E>(checkNotNull(multiset));
062      }
063    
064      private static class UnmodifiableMultiset<E>
065          extends ForwardingMultiset<E> implements Serializable {
066        final Multiset<? extends E> delegate;
067    
068        UnmodifiableMultiset(Multiset<? extends E> delegate) {
069          this.delegate = delegate;
070        }
071    
072        @SuppressWarnings("unchecked")
073        @Override protected Multiset<E> delegate() {
074          // This is safe because all non-covariant methods are overriden
075          return (Multiset) delegate;
076        }
077    
078        transient Set<E> elementSet;
079    
080        @Override public Set<E> elementSet() {
081          Set<E> es = elementSet;
082          return (es == null)
083              ? elementSet = Collections.<E>unmodifiableSet(delegate.elementSet())
084              : es;
085        }
086    
087        transient Set<Multiset.Entry<E>> entrySet;
088    
089        @SuppressWarnings("unchecked")
090        @Override public Set<Multiset.Entry<E>> entrySet() {
091          Set<Multiset.Entry<E>> es = entrySet;
092          return (es == null)
093              // Safe because the returned set is made unmodifiable and Entry
094              // itself is readonly
095              ? entrySet = (Set) Collections.unmodifiableSet(delegate.entrySet())
096              : es;
097        }
098    
099        @SuppressWarnings("unchecked")
100        @Override public Iterator<E> iterator() {
101          // Safe because the returned Iterator is made unmodifiable
102          return (Iterator) Iterators.unmodifiableIterator(delegate.iterator());
103        }
104    
105        @Override public boolean add(E element) {
106          throw new UnsupportedOperationException();
107        }
108    
109        @Override public int add(E element, int occurences) {
110          throw new UnsupportedOperationException();
111        }
112    
113        @Override public boolean addAll(Collection<? extends E> elementsToAdd) {
114          throw new UnsupportedOperationException();
115        }
116    
117        @Override public boolean remove(Object element) {
118          throw new UnsupportedOperationException();
119        }
120    
121        @Override public int remove(Object element, int occurrences) {
122          throw new UnsupportedOperationException();
123        }
124    
125        @Override public boolean removeAll(Collection<?> elementsToRemove) {
126          throw new UnsupportedOperationException();
127        }
128    
129        @Override public boolean retainAll(Collection<?> elementsToRetain) {
130          throw new UnsupportedOperationException();
131        }
132    
133        @Override public void clear() {
134          throw new UnsupportedOperationException();
135        }
136    
137        @Override public int setCount(E element, int count) {
138          throw new UnsupportedOperationException();
139        }
140    
141        @Override public boolean setCount(E element, int oldCount, int newCount) {
142          throw new UnsupportedOperationException();
143        }
144    
145        private static final long serialVersionUID = 0;
146      }
147    
148      /**
149       * Returns an immutable multiset entry with the specified element and count.
150       *
151       * @param e the element to be associated with the returned entry
152       * @param n the count to be associated with the returned entry
153       * @throws IllegalArgumentException if {@code n} is negative
154       */
155      public static <E> Multiset.Entry<E> immutableEntry(
156          @Nullable final E e, final int n) {
157        checkArgument(n >= 0);
158        return new AbstractEntry<E>() {
159          public E getElement() {
160            return e;
161          }
162          public int getCount() {
163            return n;
164          }
165        };
166      }
167    
168      /**
169       * Returns a multiset view of the specified set. The multiset is backed by the
170       * set, so changes to the set are reflected in the multiset, and vice versa.
171       * If the set is modified while an iteration over the multiset is in progress
172       * (except through the iterator's own {@code remove} operation) the results of
173       * the iteration are undefined.
174       *
175       * <p>The multiset supports element removal, which removes the corresponding
176       * element from the set. It does not support the {@code add} or {@code addAll}
177       * operations, nor does it support the use of {@code setCount} to add
178       * elements.
179       *
180       * <p>The returned multiset will be serializable if the specified set is
181       * serializable. The multiset is threadsafe if the set is threadsafe.
182       *
183       * @param set the backing set for the returned multiset view
184       */
185      static <E> Multiset<E> forSet(Set<E> set) {
186        return new SetMultiset<E>(set);
187      }
188    
189      /** @see Multisets#forSet */
190      private static class SetMultiset<E> extends ForwardingCollection<E>
191          implements Multiset<E>, Serializable {
192        final Set<E> delegate;
193    
194        SetMultiset(Set<E> set) {
195          delegate = checkNotNull(set);
196        }
197    
198        @Override protected Set<E> delegate() {
199          return delegate;
200        }
201    
202        public int count(Object element) {
203          return delegate.contains(element) ? 1 : 0;
204        }
205    
206        public int add(E element, int occurrences) {
207          throw new UnsupportedOperationException();
208        }
209    
210        public int remove(Object element, int occurrences) {
211          if (occurrences == 0) {
212            return count(element);
213          }
214          checkArgument(occurrences > 0);
215          return delegate.remove(element) ? 1 : 0;
216        }
217    
218        transient Set<E> elementSet;
219    
220        public Set<E> elementSet() {
221          Set<E> es = elementSet;
222          return (es == null) ? elementSet = new ElementSet() : es;
223        }
224    
225        transient Set<Entry<E>> entrySet;
226    
227        public Set<Entry<E>> entrySet() {
228          Set<Entry<E>> es = entrySet;
229          return (es == null) ? entrySet = new EntrySet() : es;
230        }
231    
232        @Override public boolean add(E o) {
233          throw new UnsupportedOperationException();
234        }
235    
236        @Override public boolean addAll(Collection<? extends E> c) {
237          throw new UnsupportedOperationException();
238        }
239    
240        public int setCount(E element, int count) {
241          checkNonnegative(count, "count");
242    
243          if (count == count(element)) {
244            return count;
245          } else if (count == 0) {
246            remove(element);
247            return 1;
248          } else {
249            throw new UnsupportedOperationException();
250          }
251        }
252    
253        public boolean setCount(E element, int oldCount, int newCount) {
254          return setCountImpl(this, element, oldCount, newCount);
255        }
256    
257        @Override public boolean equals(@Nullable Object object) {
258          if (object instanceof Multiset) {
259            Multiset<?> that = (Multiset<?>) object;
260            return this.size() == that.size() && delegate.equals(that.elementSet());
261          }
262          return false;
263        }
264    
265        @Override public int hashCode() {
266          int sum = 0;
267          for (E e : this) {
268            sum += ((e == null) ? 0 : e.hashCode()) ^ 1;
269          }
270          return sum;
271        }
272    
273        /** @see SetMultiset#elementSet */
274        class ElementSet extends ForwardingSet<E> {
275          @Override protected Set<E> delegate() {
276            return delegate;
277          }
278    
279          @Override public boolean add(E o) {
280            throw new UnsupportedOperationException();
281          }
282    
283          @Override public boolean addAll(Collection<? extends E> c) {
284            throw new UnsupportedOperationException();
285          }
286        }
287    
288        /** @see SetMultiset#entrySet */
289        class EntrySet extends AbstractSet<Entry<E>> {
290          @Override public int size() {
291            return delegate.size();
292          }
293          @Override public Iterator<Entry<E>> iterator() {
294            return new Iterator<Entry<E>>() {
295              final Iterator<E> elements = delegate.iterator();
296    
297              public boolean hasNext() {
298                return elements.hasNext();
299              }
300              public Entry<E> next() {
301                return immutableEntry(elements.next(), 1);
302              }
303              public void remove() {
304                elements.remove();
305              }
306            };
307          }
308          // TODO: faster contains, remove
309        }
310    
311        private static final long serialVersionUID = 0;
312      }
313    
314      /**
315       * Returns the expected number of distinct elements given the specified
316       * elements. The number of distinct elements is only computed if {@code
317       * elements} is an instance of {@code Multiset}; otherwise the default value
318       * of 11 is returned.
319       */
320      static int inferDistinctElements(Iterable<?> elements) {
321        if (elements instanceof Multiset) {
322          return ((Multiset<?>) elements).elementSet().size();
323        }
324        return 11; // initial capacity will be rounded up to 16
325      }
326    
327      /**
328       * Returns an unmodifiable <b>view</b> of the intersection of two multisets.
329       * An element's count in the multiset is the smaller of its counts in the two
330       * backing multisets. The iteration order of the returned multiset matches the
331       * element set of {@code multiset1}, with repeated occurrences of the same
332       * element appearing consecutively.
333       *
334       * <p>Results are undefined if {@code multiset1} and {@code multiset2} are
335       * based on different equivalence relations (as {@code HashMultiset} and
336       * {@code TreeMultiset} are).
337       *
338       * @since 2
339       */
340      public static <E> Multiset<E> intersection(
341          final Multiset<E> multiset1, final Multiset<?> multiset2) {
342        checkNotNull(multiset1);
343        checkNotNull(multiset2);
344    
345        return new AbstractMultiset<E>() {
346          @Override public int count(Object element) {
347            int count1 = multiset1.count(element);
348            return (count1 == 0) ? 0 : Math.min(count1, multiset2.count(element));
349          }
350    
351          @Override Set<E> createElementSet() {
352            return Sets.intersection(
353                multiset1.elementSet(), multiset2.elementSet());
354          }
355    
356          @Override public Set<Entry<E>> entrySet() {
357            return entrySet;
358          }
359    
360          final Set<Entry<E>> entrySet = new AbstractSet<Entry<E>>() {
361            @Override public Iterator<Entry<E>> iterator() {
362              final Iterator<Entry<E>> iterator1 = multiset1.entrySet().iterator();
363              return new AbstractIterator<Entry<E>>() {
364                @Override protected Entry<E> computeNext() {
365                  while (iterator1.hasNext()) {
366                    Entry<E> entry1 = iterator1.next();
367                    E element = entry1.getElement();
368                    int count
369                        = Math.min(entry1.getCount(), multiset2.count(element));
370                    if (count > 0) {
371                      return Multisets.immutableEntry(element, count);
372                    }
373                  }
374                  return endOfData();
375                }
376              };
377            }
378    
379            @Override public int size() {
380              return elementSet().size();
381            }
382    
383            @Override public boolean contains(Object o) {
384              if (o instanceof Entry) {
385                Entry<?> entry = (Entry<?>) o;
386                int entryCount = entry.getCount();
387                return (entryCount > 0)
388                    && (count(entry.getElement()) == entryCount);
389              }
390              return false;
391            }
392    
393            @Override public boolean isEmpty() {
394              return elementSet().isEmpty();
395            }
396          };
397        };
398      }
399    
400      /**
401       * Implementation of the {@code equals}, {@code hashCode}, and
402       * {@code toString} methods of {@link Multiset.Entry}.
403       */
404      abstract static class AbstractEntry<E> implements Multiset.Entry<E> {
405        /**
406         * Indicates whether an object equals this entry, following the behavior
407         * specified in {@link Multiset.Entry#equals}.
408         */
409        @Override public boolean equals(@Nullable Object object) {
410          if (object instanceof Multiset.Entry) {
411            Multiset.Entry<?> that = (Multiset.Entry<?>) object;
412            return this.getCount() == that.getCount()
413                && Objects.equal(this.getElement(), that.getElement());
414          }
415          return false;
416        }
417    
418        /**
419         * Return this entry's hash code, following the behavior specified in
420         * {@link Multiset.Entry#hashCode}.
421         */
422        @Override public int hashCode() {
423          E e = getElement();
424          return ((e == null) ? 0 : e.hashCode()) ^ getCount();
425        }
426    
427        /**
428         * Returns a string representation of this multiset entry. The string
429         * representation consists of the associated element if the associated count
430         * is one, and otherwise the associated element followed by the characters
431         * " x " (space, x and space) followed by the count. Elements and counts are
432         * converted to strings as by {@code String.valueOf}.
433         */
434        @Override public String toString() {
435          String text = String.valueOf(getElement());
436          int n = getCount();
437          return (n == 1) ? text : (text + " x " + n);
438        }
439      }
440    
441      static <E> int setCountImpl(Multiset<E> self, E element, int count) {
442        checkNonnegative(count, "count");
443    
444        int oldCount = self.count(element);
445    
446        int delta = count - oldCount;
447        if (delta > 0) {
448          self.add(element, delta);
449        } else if (delta < 0) {
450          self.remove(element, -delta);
451        }
452    
453        return oldCount;
454      }
455    
456      static <E> boolean setCountImpl(
457          Multiset<E> self, E element, int oldCount, int newCount) {
458        checkNonnegative(oldCount, "oldCount");
459        checkNonnegative(newCount, "newCount");
460    
461        if (self.count(element) == oldCount) {
462          self.setCount(element, newCount);
463          return true;
464        } else {
465          return false;
466        }
467      }
468    
469      static void checkNonnegative(int count, String name) {
470        checkArgument(count >= 0, "%s cannot be negative: %s", name, count);
471      }
472    }