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