001/*
002 * Copyright (C) 2008 The Guava Authors
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
017package com.google.common.collect;
018
019import static com.google.common.base.Preconditions.checkNotNull;
020
021import com.google.common.annotations.GwtCompatible;
022import com.google.common.primitives.Ints;
023
024import java.io.Serializable;
025import java.util.ArrayList;
026import java.util.Arrays;
027import java.util.Collection;
028import java.util.Collections;
029import java.util.Iterator;
030import java.util.List;
031
032import javax.annotation.Nullable;
033
034/**
035 * An immutable hash-based multiset. Does not permit null elements.
036 *
037 * <p>Its iterator orders elements according to the first appearance of the
038 * element among the items passed to the factory method or builder. When the
039 * multiset contains multiple instances of an element, those instances are
040 * consecutive in the iteration order.
041 *
042 * <p>See the Guava User Guide article on <a href=
043 * "http://code.google.com/p/guava-libraries/wiki/ImmutableCollectionsExplained">
044 * immutable collections</a>.
045 *
046 * @author Jared Levy
047 * @author Louis Wasserman
048 * @since 2.0 (imported from Google Collections Library)
049 */
050@GwtCompatible(serializable = true)
051@SuppressWarnings("serial") // we're overriding default serialization
052// TODO(user): write an efficient asList() implementation
053public abstract class ImmutableMultiset<E> extends ImmutableCollection<E>
054    implements Multiset<E> {
055
056  /**
057   * Returns the empty immutable multiset.
058   */
059  @SuppressWarnings("unchecked") // all supported methods are covariant
060  public static <E> ImmutableMultiset<E> of() {
061    return (ImmutableMultiset<E>) EmptyImmutableMultiset.INSTANCE;
062  }
063
064  /**
065   * Returns an immutable multiset containing a single element.
066   *
067   * @throws NullPointerException if {@code element} is null
068   * @since 6.0 (source-compatible since 2.0)
069   */
070  @SuppressWarnings("unchecked") // generic array created but never written
071  public static <E> ImmutableMultiset<E> of(E element) {
072    return copyOfInternal(element);
073  }
074
075  /**
076   * Returns an immutable multiset containing the given elements, in order.
077   *
078   * @throws NullPointerException if any element is null
079   * @since 6.0 (source-compatible since 2.0)
080   */
081  @SuppressWarnings("unchecked") //
082  public static <E> ImmutableMultiset<E> of(E e1, E e2) {
083    return copyOfInternal(e1, e2);
084  }
085
086  /**
087   * Returns an immutable multiset containing the given elements, in order.
088   *
089   * @throws NullPointerException if any element is null
090   * @since 6.0 (source-compatible since 2.0)
091   */
092  @SuppressWarnings("unchecked") //
093  public static <E> ImmutableMultiset<E> of(E e1, E e2, E e3) {
094    return copyOfInternal(e1, e2, e3);
095  }
096
097  /**
098   * Returns an immutable multiset containing the given elements, in order.
099   *
100   * @throws NullPointerException if any element is null
101   * @since 6.0 (source-compatible since 2.0)
102   */
103  @SuppressWarnings("unchecked") //
104  public static <E> ImmutableMultiset<E> of(E e1, E e2, E e3, E e4) {
105    return copyOfInternal(e1, e2, e3, e4);
106  }
107
108  /**
109   * Returns an immutable multiset containing the given elements, in order.
110   *
111   * @throws NullPointerException if any element is null
112   * @since 6.0 (source-compatible since 2.0)
113   */
114  @SuppressWarnings("unchecked") //
115  public static <E> ImmutableMultiset<E> of(E e1, E e2, E e3, E e4, E e5) {
116    return copyOfInternal(e1, e2, e3, e4, e5);
117  }
118
119  /**
120   * Returns an immutable multiset containing the given elements, in order.
121   *
122   * @throws NullPointerException if any element is null
123   * @since 6.0 (source-compatible since 2.0)
124   */
125  @SuppressWarnings("unchecked") //
126  public static <E> ImmutableMultiset<E> of(
127      E e1, E e2, E e3, E e4, E e5, E e6, E... others) {
128    int size = others.length + 6;
129    List<E> all = new ArrayList<E>(size);
130    Collections.addAll(all, e1, e2, e3, e4, e5, e6);
131    Collections.addAll(all, others);
132    return copyOf(all);
133  }
134
135  /**
136   * Returns an immutable multiset containing the given elements.
137   *
138   * <p>The multiset is ordered by the first occurrence of each element. For
139   * example, {@code ImmutableMultiset.copyOf([2, 3, 1, 3])} yields a multiset
140   * with elements in the order {@code 2, 3, 3, 1}.
141   *
142   * @throws NullPointerException if any of {@code elements} is null
143   * @since 6.0
144   */
145  public static <E> ImmutableMultiset<E> copyOf(E[] elements) {
146    return copyOf(Arrays.asList(elements));
147  }
148
149  /**
150   * Returns an immutable multiset containing the given elements.
151   *
152   * <p>The multiset is ordered by the first occurrence of each element. For
153   * example, {@code ImmutableMultiset.copyOf(Arrays.asList(2, 3, 1, 3))} yields
154   * a multiset with elements in the order {@code 2, 3, 3, 1}.
155   *
156   * <p>Despite the method name, this method attempts to avoid actually copying
157   * the data when it is safe to do so. The exact circumstances under which a
158   * copy will or will not be performed are undocumented and subject to change.
159   *
160   * <p><b>Note:</b> Despite what the method name suggests, if {@code elements}
161   * is an {@code ImmutableMultiset}, no copy will actually be performed, and
162   * the given multiset itself will be returned.
163   *
164   * @throws NullPointerException if any of {@code elements} is null
165   */
166  public static <E> ImmutableMultiset<E> copyOf(
167      Iterable<? extends E> elements) {
168    if (elements instanceof ImmutableMultiset) {
169      @SuppressWarnings("unchecked") // all supported methods are covariant
170      ImmutableMultiset<E> result = (ImmutableMultiset<E>) elements;
171      if (!result.isPartialView()) {
172        return result;
173      }
174    }
175
176    Multiset<? extends E> multiset = (elements instanceof Multiset)
177        ? Multisets.cast(elements)
178        : LinkedHashMultiset.create(elements);
179
180    return copyOfInternal(multiset);
181  }
182
183  private static <E> ImmutableMultiset<E> copyOfInternal(E... elements) {
184    return copyOf(Arrays.asList(elements));
185  }
186
187  private static <E> ImmutableMultiset<E> copyOfInternal(
188      Multiset<? extends E> multiset) {
189    return copyFromEntries(multiset.entrySet());
190  }
191
192  static <E> ImmutableMultiset<E> copyFromEntries(
193      Collection<? extends Entry<? extends E>> entries) {
194    long size = 0;
195    ImmutableMap.Builder<E, Integer> builder = ImmutableMap.builder();
196    for (Entry<? extends E> entry : entries) {
197      int count = entry.getCount();
198      if (count > 0) {
199        // Since ImmutableMap.Builder throws an NPE if an element is null, no
200        // other null checks are needed.
201        builder.put(entry.getElement(), count);
202        size += count;
203      }
204    }
205
206    if (size == 0) {
207      return of();
208    }
209    return new RegularImmutableMultiset<E>(
210        builder.build(), Ints.saturatedCast(size));
211  }
212
213  /**
214   * Returns an immutable multiset containing the given elements.
215   *
216   * <p>The multiset is ordered by the first occurrence of each element. For
217   * example,
218   * {@code ImmutableMultiset.copyOf(Arrays.asList(2, 3, 1, 3).iterator())}
219   * yields a multiset with elements in the order {@code 2, 3, 3, 1}.
220   *
221   * @throws NullPointerException if any of {@code elements} is null
222   */
223  public static <E> ImmutableMultiset<E> copyOf(
224      Iterator<? extends E> elements) {
225    Multiset<E> multiset = LinkedHashMultiset.create();
226    Iterators.addAll(multiset, elements);
227    return copyOfInternal(multiset);
228  }
229
230  ImmutableMultiset() {}
231
232  @Override public UnmodifiableIterator<E> iterator() {
233    final Iterator<Entry<E>> entryIterator = entrySet().iterator();
234    return new UnmodifiableIterator<E>() {
235      int remaining;
236      E element;
237
238      @Override
239      public boolean hasNext() {
240        return (remaining > 0) || entryIterator.hasNext();
241      }
242
243      @Override
244      public E next() {
245        if (remaining <= 0) {
246          Entry<E> entry = entryIterator.next();
247          element = entry.getElement();
248          remaining = entry.getCount();
249        }
250        remaining--;
251        return element;
252      }
253    };
254  }
255
256  @Override
257  public boolean contains(@Nullable Object object) {
258    return count(object) > 0;
259  }
260
261  @Override
262  public boolean containsAll(Collection<?> targets) {
263    return elementSet().containsAll(targets);
264  }
265
266  /**
267   * Guaranteed to throw an exception and leave the collection unmodified.
268   *
269   * @throws UnsupportedOperationException always
270   * @deprecated Unsupported operation.
271   */
272  @Deprecated
273  @Override
274  public final int add(E element, int occurrences) {
275    throw new UnsupportedOperationException();
276  }
277
278  /**
279   * Guaranteed to throw an exception and leave the collection unmodified.
280   *
281   * @throws UnsupportedOperationException always
282   * @deprecated Unsupported operation.
283   */
284  @Deprecated
285  @Override
286  public final int remove(Object element, int occurrences) {
287    throw new UnsupportedOperationException();
288  }
289
290  /**
291   * Guaranteed to throw an exception and leave the collection unmodified.
292   *
293   * @throws UnsupportedOperationException always
294   * @deprecated Unsupported operation.
295   */
296  @Deprecated
297  @Override
298  public final int setCount(E element, int count) {
299    throw new UnsupportedOperationException();
300  }
301
302  /**
303   * Guaranteed to throw an exception and leave the collection unmodified.
304   *
305   * @throws UnsupportedOperationException always
306   * @deprecated Unsupported operation.
307   */
308  @Deprecated
309  @Override
310  public final boolean setCount(E element, int oldCount, int newCount) {
311    throw new UnsupportedOperationException();
312  }
313
314  @Override public boolean equals(@Nullable Object object) {
315    if (object == this) {
316      return true;
317    }
318    if (object instanceof Multiset) {
319      Multiset<?> that = (Multiset<?>) object;
320      if (this.size() != that.size()) {
321        return false;
322      }
323      for (Entry<?> entry : that.entrySet()) {
324        if (count(entry.getElement()) != entry.getCount()) {
325          return false;
326        }
327      }
328      return true;
329    }
330    return false;
331  }
332
333  @Override public int hashCode() {
334    return Sets.hashCodeImpl(entrySet());
335  }
336
337  @Override public String toString() {
338    return entrySet().toString();
339  }
340
341  private transient ImmutableSet<Entry<E>> entrySet;
342
343  @Override
344  public ImmutableSet<Entry<E>> entrySet() {
345    ImmutableSet<Entry<E>> es = entrySet;
346    return (es == null) ? (entrySet = createEntrySet()) : es;
347  }
348
349  abstract ImmutableSet<Entry<E>> createEntrySet();
350
351  abstract class EntrySet extends ImmutableSet<Entry<E>> {
352    @Override
353    boolean isPartialView() {
354      return ImmutableMultiset.this.isPartialView();
355    }
356
357    @Override
358    public boolean contains(Object o) {
359      if (o instanceof Entry) {
360        Entry<?> entry = (Entry<?>) o;
361        if (entry.getCount() <= 0) {
362          return false;
363        }
364        int count = count(entry.getElement());
365        return count == entry.getCount();
366      }
367      return false;
368    }
369
370    /*
371     * TODO(hhchan): Revert once we have a separate, manual emulation of this
372     * class.
373     */
374    @Override
375    public Object[] toArray() {
376      Object[] newArray = new Object[size()];
377      return toArray(newArray);
378    }
379
380    /*
381     * TODO(hhchan): Revert once we have a separate, manual emulation of this
382     * class.
383     */
384    @Override
385    public <T> T[] toArray(T[] other) {
386      int size = size();
387      if (other.length < size) {
388        other = ObjectArrays.newArray(other, size);
389      } else if (other.length > size) {
390        other[size] = null;
391      }
392
393      // Writes will produce ArrayStoreException when the toArray() doc requires
394      Object[] otherAsObjectArray = other;
395      int index = 0;
396      for (Entry<?> element : this) {
397        otherAsObjectArray[index++] = element;
398      }
399      return other;
400    }
401
402    @Override
403    public int hashCode() {
404      return ImmutableMultiset.this.hashCode();
405    }
406
407    // We can't label this with @Override, because it doesn't override anything
408    // in the GWT emulated version.
409    // TODO(cpovirk): try making all copies of this method @GwtIncompatible instead
410    Object writeReplace() {
411      return new EntrySetSerializedForm<E>(ImmutableMultiset.this);
412    }
413
414    private static final long serialVersionUID = 0;
415  }
416
417  static class EntrySetSerializedForm<E> implements Serializable {
418    final ImmutableMultiset<E> multiset;
419
420    EntrySetSerializedForm(ImmutableMultiset<E> multiset) {
421      this.multiset = multiset;
422    }
423
424    Object readResolve() {
425      return multiset.entrySet();
426    }
427  }
428
429  private static class SerializedForm implements Serializable {
430    final Object[] elements;
431    final int[] counts;
432
433    SerializedForm(Multiset<?> multiset) {
434      int distinct = multiset.entrySet().size();
435      elements = new Object[distinct];
436      counts = new int[distinct];
437      int i = 0;
438      for (Entry<?> entry : multiset.entrySet()) {
439        elements[i] = entry.getElement();
440        counts[i] = entry.getCount();
441        i++;
442      }
443    }
444
445    Object readResolve() {
446      LinkedHashMultiset<Object> multiset =
447          LinkedHashMultiset.create(elements.length);
448      for (int i = 0; i < elements.length; i++) {
449        multiset.add(elements[i], counts[i]);
450      }
451      return ImmutableMultiset.copyOf(multiset);
452    }
453
454    private static final long serialVersionUID = 0;
455  }
456
457  // We can't label this with @Override, because it doesn't override anything
458  // in the GWT emulated version.
459  Object writeReplace() {
460    return new SerializedForm(this);
461  }
462
463  /**
464   * Returns a new builder. The generated builder is equivalent to the builder
465   * created by the {@link Builder} constructor.
466   */
467  public static <E> Builder<E> builder() {
468    return new Builder<E>();
469  }
470
471  /**
472   * A builder for creating immutable multiset instances, especially {@code
473   * public static final} multisets ("constant multisets"). Example:
474   * <pre> {@code
475   *
476   *   public static final ImmutableMultiset<Bean> BEANS =
477   *       new ImmutableMultiset.Builder<Bean>()
478   *           .addCopies(Bean.COCOA, 4)
479   *           .addCopies(Bean.GARDEN, 6)
480   *           .addCopies(Bean.RED, 8)
481   *           .addCopies(Bean.BLACK_EYED, 10)
482   *           .build();}</pre>
483   *
484   * Builder instances can be reused; it is safe to call {@link #build} multiple
485   * times to build multiple multisets in series.
486   *
487   * @since 2.0 (imported from Google Collections Library)
488   */
489  public static class Builder<E> extends ImmutableCollection.Builder<E> {
490    final Multiset<E> contents;
491
492    /**
493     * Creates a new builder. The returned builder is equivalent to the builder
494     * generated by {@link ImmutableMultiset#builder}.
495     */
496    public Builder() {
497      this(LinkedHashMultiset.<E>create());
498    }
499
500    Builder(Multiset<E> contents) {
501      this.contents = contents;
502    }
503
504    /**
505     * Adds {@code element} to the {@code ImmutableMultiset}.
506     *
507     * @param element the element to add
508     * @return this {@code Builder} object
509     * @throws NullPointerException if {@code element} is null
510     */
511    @Override public Builder<E> add(E element) {
512      contents.add(checkNotNull(element));
513      return this;
514    }
515
516    /**
517     * Adds a number of occurrences of an element to this {@code
518     * ImmutableMultiset}.
519     *
520     * @param element the element to add
521     * @param occurrences the number of occurrences of the element to add. May
522     *     be zero, in which case no change will be made.
523     * @return this {@code Builder} object
524     * @throws NullPointerException if {@code element} is null
525     * @throws IllegalArgumentException if {@code occurrences} is negative, or
526     *     if this operation would result in more than {@link Integer#MAX_VALUE}
527     *     occurrences of the element
528     */
529    public Builder<E> addCopies(E element, int occurrences) {
530      contents.add(checkNotNull(element), occurrences);
531      return this;
532    }
533
534    /**
535     * Adds or removes the necessary occurrences of an element such that the
536     * element attains the desired count.
537     *
538     * @param element the element to add or remove occurrences of
539     * @param count the desired count of the element in this multiset
540     * @return this {@code Builder} object
541     * @throws NullPointerException if {@code element} is null
542     * @throws IllegalArgumentException if {@code count} is negative
543     */
544    public Builder<E> setCount(E element, int count) {
545      contents.setCount(checkNotNull(element), count);
546      return this;
547    }
548
549    /**
550     * Adds each element of {@code elements} to the {@code ImmutableMultiset}.
551     *
552     * @param elements the elements to add
553     * @return this {@code Builder} object
554     * @throws NullPointerException if {@code elements} is null or contains a
555     *     null element
556     */
557    @Override public Builder<E> add(E... elements) {
558      super.add(elements);
559      return this;
560    }
561
562    /**
563     * Adds each element of {@code elements} to the {@code ImmutableMultiset}.
564     *
565     * @param elements the {@code Iterable} to add to the {@code
566     *     ImmutableMultiset}
567     * @return this {@code Builder} object
568     * @throws NullPointerException if {@code elements} is null or contains a
569     *     null element
570     */
571    @Override public Builder<E> addAll(Iterable<? extends E> elements) {
572      if (elements instanceof Multiset) {
573        Multiset<? extends E> multiset = Multisets.cast(elements);
574        for (Entry<? extends E> entry : multiset.entrySet()) {
575          addCopies(entry.getElement(), entry.getCount());
576        }
577      } else {
578        super.addAll(elements);
579      }
580      return this;
581    }
582
583    /**
584     * Adds each element of {@code elements} to the {@code ImmutableMultiset}.
585     *
586     * @param elements the elements to add to the {@code ImmutableMultiset}
587     * @return this {@code Builder} object
588     * @throws NullPointerException if {@code elements} is null or contains a
589     *     null element
590     */
591    @Override public Builder<E> addAll(Iterator<? extends E> elements) {
592      super.addAll(elements);
593      return this;
594    }
595
596    /**
597     * Returns a newly-created {@code ImmutableMultiset} based on the contents
598     * of the {@code Builder}.
599     */
600    @Override public ImmutableMultiset<E> build() {
601      return copyOf(contents);
602    }
603  }
604}