001    /*
002     * Copyright (C) 2007 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    
017    package com.google.common.collect;
018    
019    import static com.google.common.base.Preconditions.checkNotNull;
020    
021    import com.google.common.annotations.GwtCompatible;
022    import com.google.common.annotations.GwtIncompatible;
023    
024    import java.io.IOException;
025    import java.io.ObjectInputStream;
026    import java.io.ObjectOutputStream;
027    import java.util.Comparator;
028    import java.util.Iterator;
029    import java.util.Set;
030    import java.util.SortedMap;
031    import java.util.SortedSet;
032    import java.util.TreeMap;
033    
034    import javax.annotation.Nullable;
035    
036    /**
037     * A multiset which maintains the ordering of its elements, according to either
038     * their natural order or an explicit {@link Comparator}. In all cases, this
039     * implementation uses {@link Comparable#compareTo} or {@link
040     * Comparator#compare} instead of {@link Object#equals} to determine
041     * equivalence of instances.
042     *
043     * <p><b>Warning:</b> The comparison must be <i>consistent with equals</i> as
044     * explained by the {@link Comparable} class specification. Otherwise, the
045     * resulting multiset will violate the {@link java.util.Collection} contract,
046     * which is specified in terms of {@link Object#equals}.
047     *
048     * @author Neal Kanodia
049     * @author Jared Levy
050     * @since 2.0 (imported from Google Collections Library)
051     */
052    @GwtCompatible(emulated = true)
053    @SuppressWarnings("serial") // we're overriding default serialization
054    public final class TreeMultiset<E> extends AbstractMapBasedMultiset<E>
055        implements SortedIterable<E> {
056    
057      /**
058       * Creates a new, empty multiset, sorted according to the elements' natural
059       * order. All elements inserted into the multiset must implement the
060       * {@code Comparable} interface. Furthermore, all such elements must be
061       * <i>mutually comparable</i>: {@code e1.compareTo(e2)} must not throw a
062       * {@code ClassCastException} for any elements {@code e1} and {@code e2} in
063       * the multiset. If the user attempts to add an element to the multiset that
064       * violates this constraint (for example, the user attempts to add a string
065       * element to a set whose elements are integers), the {@code add(Object)}
066       * call will throw a {@code ClassCastException}.
067       *
068       * <p>The type specification is {@code <E extends Comparable>}, instead of the
069       * more specific {@code <E extends Comparable<? super E>>}, to support
070       * classes defined without generics.
071       */
072      public static <E extends Comparable> TreeMultiset<E> create() {
073        return new TreeMultiset<E>();
074      }
075    
076      /**
077       * Creates a new, empty multiset, sorted according to the specified
078       * comparator. All elements inserted into the multiset must be <i>mutually
079       * comparable</i> by the specified comparator: {@code comparator.compare(e1,
080       * e2)} must not throw a {@code ClassCastException} for any elements {@code
081       * e1} and {@code e2} in the multiset. If the user attempts to add an element
082       * to the multiset that violates this constraint, the {@code add(Object)} call
083       * will throw a {@code ClassCastException}.
084       *
085       * @param comparator the comparator that will be used to sort this multiset. A
086       *     null value indicates that the elements' <i>natural ordering</i> should
087       *     be used.
088       */
089      public static <E> TreeMultiset<E> create(
090          @Nullable Comparator<? super E> comparator) {
091    
092        return (comparator == null) 
093               ? new TreeMultiset<E>()
094               : new TreeMultiset<E>(comparator);
095      }
096    
097      /**
098       * Returns an iterator over the elements contained in this collection.
099       */
100      @Override
101      public Iterator<E> iterator() {
102        // Needed to avoid Javadoc bug.
103        return super.iterator();
104      }
105    
106      /**
107       * Creates an empty multiset containing the given initial elements, sorted
108       * according to the elements' natural order.
109       *
110       * <p>This implementation is highly efficient when {@code elements} is itself
111       * a {@link Multiset}.
112       *
113       * <p>The type specification is {@code <E extends Comparable>}, instead of the
114       * more specific {@code <E extends Comparable<? super E>>}, to support
115       * classes defined without generics.
116       */
117      public static <E extends Comparable> TreeMultiset<E> create(
118          Iterable<? extends E> elements) {
119        TreeMultiset<E> multiset = create();
120        Iterables.addAll(multiset, elements);
121        return multiset;
122      }
123    
124      private final Comparator<? super E> comparator;
125      
126      @SuppressWarnings("unchecked")
127      private TreeMultiset() {
128        this((Comparator) Ordering.natural());
129      }
130    
131      private TreeMultiset(@Nullable Comparator<? super E> comparator) {
132        super(new TreeMap<E, Count>(checkNotNull(comparator)));
133        this.comparator = comparator;
134      }
135    
136      /**
137       * Returns the comparator associated with this multiset.
138       */
139      @Override
140      public Comparator<? super E> comparator() {
141        return comparator;
142      }
143    
144      /**
145       * {@inheritDoc}
146       *
147       * <p>In {@code TreeMultiset}, the return type of this method is narrowed
148       * from {@link Set} to {@link SortedSet}.
149       */
150      @Override public SortedSet<E> elementSet() {
151        return (SortedSet<E>) super.elementSet();
152      }
153    
154      @Override public int count(@Nullable Object element) {
155        try {
156          return super.count(element);
157        } catch (NullPointerException e) {
158          return 0;
159        } catch (ClassCastException e) {
160          return 0;
161        }
162      }
163    
164      @Override
165      public int add(E element, int occurrences) {
166        if (element == null) {
167          comparator.compare(element, element);
168        }
169        return super.add(element, occurrences);
170      }
171    
172      @Override Set<E> createElementSet() {
173        return new SortedMapBasedElementSet(
174            (SortedMap<E, Count>) backingMap());
175      }
176    
177      private class SortedMapBasedElementSet extends MapBasedElementSet
178          implements SortedSet<E>, SortedIterable<E> {
179    
180        SortedMapBasedElementSet(SortedMap<E, Count> map) {
181          super(map);
182        }
183    
184        SortedMap<E, Count> sortedMap() {
185          return (SortedMap<E, Count>) getMap();
186        }
187    
188        /**
189         * {@inheritDoc}
190         *
191         * @since 10.0
192         */
193        @Override
194        public Comparator<? super E> comparator() {
195          return sortedMap().comparator();
196        }
197    
198        @Override
199        public E first() {
200          return sortedMap().firstKey();
201        }
202    
203        @Override
204        public E last() {
205          return sortedMap().lastKey();
206        }
207    
208        @Override
209        public SortedSet<E> headSet(E toElement) {
210          return new SortedMapBasedElementSet(sortedMap().headMap(toElement));
211        }
212    
213        @Override
214        public SortedSet<E> subSet(E fromElement, E toElement) {
215          return new SortedMapBasedElementSet(
216              sortedMap().subMap(fromElement, toElement));
217        }
218    
219        @Override
220        public SortedSet<E> tailSet(E fromElement) {
221          return new SortedMapBasedElementSet(sortedMap().tailMap(fromElement));
222        }
223    
224        @Override public boolean remove(Object element) {
225          try {
226            return super.remove(element);
227          } catch (NullPointerException e) {
228            return false;
229          } catch (ClassCastException e) {
230            return false;
231          }
232        }
233      }
234    
235      /*
236       * TODO(jlevy): Decide whether entrySet() should return entries with an
237       * equals() method that calls the comparator to compare the two keys. If that
238       * change is made, AbstractMultiset.equals() can simply check whether two
239       * multisets have equal entry sets.
240       */
241    
242      /**
243       * @serialData the comparator, the number of distinct elements, the first
244       *     element, its count, the second element, its count, and so on
245       */
246      @GwtIncompatible("java.io.ObjectOutputStream")
247      private void writeObject(ObjectOutputStream stream) throws IOException {
248        stream.defaultWriteObject();
249        stream.writeObject(elementSet().comparator());
250        Serialization.writeMultiset(this, stream);
251      }
252    
253      @GwtIncompatible("java.io.ObjectInputStream")
254      private void readObject(ObjectInputStream stream)
255          throws IOException, ClassNotFoundException {
256        stream.defaultReadObject();
257        @SuppressWarnings("unchecked") // reading data stored by writeObject
258        Comparator<? super E> comparator
259            = (Comparator<? super E>) stream.readObject();
260        setBackingMap(new TreeMap<E, Count>(comparator));
261        Serialization.populateMultiset(this, stream);
262      }
263    
264      @GwtIncompatible("not needed in emulated source")
265      private static final long serialVersionUID = 0;
266    }