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       * @since 11.0
140       */
141      @Override
142      public Comparator<? super E> comparator() {
143        return comparator;
144      }
145    
146      /**
147       * {@inheritDoc}
148       *
149       * <p>In {@code TreeMultiset}, the return type of this method is narrowed
150       * from {@link Set} to {@link SortedSet}.
151       */
152      @Override public SortedSet<E> elementSet() {
153        return (SortedSet<E>) super.elementSet();
154      }
155    
156      @Override public int count(@Nullable Object element) {
157        try {
158          return super.count(element);
159        } catch (NullPointerException e) {
160          return 0;
161        } catch (ClassCastException e) {
162          return 0;
163        }
164      }
165    
166      @Override
167      public int add(E element, int occurrences) {
168        if (element == null) {
169          comparator.compare(element, element);
170        }
171        return super.add(element, occurrences);
172      }
173    
174      @Override Set<E> createElementSet() {
175        return new SortedMapBasedElementSet(
176            (SortedMap<E, Count>) backingMap());
177      }
178    
179      private class SortedMapBasedElementSet extends MapBasedElementSet
180          implements SortedSet<E>, SortedIterable<E> {
181    
182        SortedMapBasedElementSet(SortedMap<E, Count> map) {
183          super(map);
184        }
185    
186        SortedMap<E, Count> sortedMap() {
187          return (SortedMap<E, Count>) getMap();
188        }
189    
190        /**
191         * {@inheritDoc}
192         *
193         * @since 10.0
194         */
195        @Override
196        public Comparator<? super E> comparator() {
197          return sortedMap().comparator();
198        }
199    
200        @Override
201        public E first() {
202          return sortedMap().firstKey();
203        }
204    
205        @Override
206        public E last() {
207          return sortedMap().lastKey();
208        }
209    
210        @Override
211        public SortedSet<E> headSet(E toElement) {
212          return new SortedMapBasedElementSet(sortedMap().headMap(toElement));
213        }
214    
215        @Override
216        public SortedSet<E> subSet(E fromElement, E toElement) {
217          return new SortedMapBasedElementSet(
218              sortedMap().subMap(fromElement, toElement));
219        }
220    
221        @Override
222        public SortedSet<E> tailSet(E fromElement) {
223          return new SortedMapBasedElementSet(sortedMap().tailMap(fromElement));
224        }
225    
226        @Override public boolean remove(Object element) {
227          try {
228            return super.remove(element);
229          } catch (NullPointerException e) {
230            return false;
231          } catch (ClassCastException e) {
232            return false;
233          }
234        }
235      }
236    
237      /*
238       * TODO(jlevy): Decide whether entrySet() should return entries with an
239       * equals() method that calls the comparator to compare the two keys. If that
240       * change is made, AbstractMultiset.equals() can simply check whether two
241       * multisets have equal entry sets.
242       */
243    
244      /**
245       * @serialData the comparator, the number of distinct elements, the first
246       *     element, its count, the second element, its count, and so on
247       */
248      @GwtIncompatible("java.io.ObjectOutputStream")
249      private void writeObject(ObjectOutputStream stream) throws IOException {
250        stream.defaultWriteObject();
251        stream.writeObject(elementSet().comparator());
252        Serialization.writeMultiset(this, stream);
253      }
254    
255      @GwtIncompatible("java.io.ObjectInputStream")
256      private void readObject(ObjectInputStream stream)
257          throws IOException, ClassNotFoundException {
258        stream.defaultReadObject();
259        @SuppressWarnings("unchecked") // reading data stored by writeObject
260        Comparator<? super E> comparator
261            = (Comparator<? super E>) stream.readObject();
262        setBackingMap(new TreeMap<E, Count>(comparator));
263        Serialization.populateMultiset(this, stream);
264      }
265    
266      @GwtIncompatible("not needed in emulated source")
267      private static final long serialVersionUID = 0;
268    }