001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.collections4.multiset;
018
019import java.io.IOException;
020import java.io.ObjectInputStream;
021import java.io.ObjectOutputStream;
022import java.lang.reflect.Array;
023import java.util.ConcurrentModificationException;
024import java.util.Iterator;
025import java.util.Map;
026
027import org.apache.commons.collections4.MultiSet;
028import org.apache.commons.collections4.iterators.AbstractIteratorDecorator;
029
030/**
031 * Abstract implementation of the {@link MultiSet} interface to simplify the
032 * creation of subclass implementations.
033 * <p>
034 * Subclasses specify a Map implementation to use as the internal storage. The
035 * map will be used to map multiset elements to a number; the number represents the
036 * number of occurrences of that element in the multiset.
037 * </p>
038 *
039 * @param <E> the type held in the multiset
040 * @since 4.1
041 */
042public abstract class AbstractMapMultiSet<E> extends AbstractMultiSet<E> {
043
044    /**
045     * Inner class EntrySetIterator.
046     */
047    protected static class EntrySetIterator<E> implements Iterator<Entry<E>> {
048
049        /** The parent map */
050        protected final AbstractMapMultiSet<E> parent;
051
052        protected final Iterator<Map.Entry<E, MutableInteger>> decorated;
053
054        /** The last returned entry */
055        protected Entry<E> last;
056
057        /** Whether remove is allowed at present */
058        protected boolean canRemove;
059
060        /**
061         * Constructs a new instance.
062         * @param iterator  the iterator to decorate
063         * @param parent  the parent multiset
064         */
065        protected EntrySetIterator(final Iterator<Map.Entry<E, MutableInteger>> iterator,
066                                   final AbstractMapMultiSet<E> parent) {
067            this.decorated = iterator;
068            this.parent = parent;
069        }
070
071        @Override
072        public boolean hasNext() {
073            return decorated.hasNext();
074        }
075
076        @Override
077        public Entry<E> next() {
078            last = new MultiSetEntry<>(decorated.next());
079            canRemove = true;
080            return last;
081        }
082
083        @Override
084        public void remove() {
085            if (!canRemove) {
086                throw new IllegalStateException("Iterator remove() can only be called once after next()");
087            }
088            decorated.remove();
089            last = null;
090            canRemove = false;
091        }
092    }
093    /**
094     * Inner class iterator for the MultiSet.
095     */
096    private static final class MapBasedMultiSetIterator<E> implements Iterator<E> {
097        private final AbstractMapMultiSet<E> parent;
098        private final Iterator<Map.Entry<E, MutableInteger>> entryIterator;
099        private Map.Entry<E, MutableInteger> current;
100        private int itemCount;
101        private final int mods;
102        private boolean canRemove;
103
104        /**
105         * Constructs a new instance.
106         *
107         * @param parent the parent multiset
108         */
109        MapBasedMultiSetIterator(final AbstractMapMultiSet<E> parent) {
110            this.parent = parent;
111            this.entryIterator = parent.map.entrySet().iterator();
112            this.current = null;
113            this.mods = parent.modCount;
114            this.canRemove = false;
115        }
116
117        /** {@inheritDoc} */
118        @Override
119        public boolean hasNext() {
120            return itemCount > 0 || entryIterator.hasNext();
121        }
122
123        /** {@inheritDoc} */
124        @Override
125        public E next() {
126            if (parent.modCount != mods) {
127                throw new ConcurrentModificationException();
128            }
129            if (itemCount == 0) {
130                current = entryIterator.next();
131                itemCount = current.getValue().value;
132            }
133            canRemove = true;
134            itemCount--;
135            return current.getKey();
136        }
137
138        /** {@inheritDoc} */
139        @Override
140        public void remove() {
141            if (parent.modCount != mods) {
142                throw new ConcurrentModificationException();
143            }
144            if (!canRemove) {
145                throw new IllegalStateException();
146            }
147            final MutableInteger mut = current.getValue();
148            if (mut.value > 1) {
149                mut.value--;
150            } else {
151                entryIterator.remove();
152            }
153            parent.size--;
154            canRemove = false;
155        }
156    }
157    /**
158     * Inner class MultiSetEntry.
159     */
160    protected static class MultiSetEntry<E> extends AbstractEntry<E> {
161
162        protected final Map.Entry<E, MutableInteger> parentEntry;
163
164        /**
165         * Constructs a new instance.
166         * @param parentEntry  the entry to decorate
167         */
168        protected MultiSetEntry(final Map.Entry<E, MutableInteger> parentEntry) {
169            this.parentEntry = parentEntry;
170        }
171
172        @Override
173        public int getCount() {
174            return parentEntry.getValue().value;
175        }
176
177        @Override
178        public E getElement() {
179            return parentEntry.getKey();
180        }
181    }
182
183    /**
184     * Mutable integer class for storing the data.
185     */
186    protected static class MutableInteger {
187        /** The value of this mutable. */
188        protected int value;
189
190        /**
191         * Constructs a new instance.
192         * @param value the initial value
193         */
194        MutableInteger(final int value) {
195            this.value = value;
196        }
197
198        @Override
199        public boolean equals(final Object obj) {
200            if (!(obj instanceof MutableInteger)) {
201                return false;
202            }
203            return ((MutableInteger) obj).value == value;
204        }
205
206        @Override
207        public int hashCode() {
208            return value;
209        }
210    }
211
212    /**
213     * Inner class UniqueSetIterator.
214     */
215    protected static class UniqueSetIterator<E> extends AbstractIteratorDecorator<E> {
216
217        /** The parent multiset */
218        protected final AbstractMapMultiSet<E> parent;
219
220        /** The last returned element */
221        protected E lastElement;
222
223        /** Whether remove is allowed at present */
224        protected boolean canRemove;
225
226        /**
227         * Constructs a new instance.
228         * @param iterator  the iterator to decorate
229         * @param parent  the parent multiset
230         */
231        protected UniqueSetIterator(final Iterator<E> iterator, final AbstractMapMultiSet<E> parent) {
232            super(iterator);
233            this.parent = parent;
234        }
235
236        @Override
237        public E next() {
238            lastElement = super.next();
239            canRemove = true;
240            return lastElement;
241        }
242
243        @Override
244        public void remove() {
245            if (!canRemove) {
246                throw new IllegalStateException("Iterator remove() can only be called once after next()");
247            }
248            final int count = parent.getCount(lastElement);
249            super.remove();
250            parent.remove(lastElement, count);
251            lastElement = null;
252            canRemove = false;
253        }
254    }
255
256    /** The map to use to store the data */
257    private transient Map<E, MutableInteger> map;
258
259    /** The current total size of the multiset */
260    private transient int size;
261
262    /** The modification count for fail fast iterators */
263    private transient int modCount;
264
265    /**
266     * Constructor needed for subclass serialisation.
267     */
268    protected AbstractMapMultiSet() {
269    }
270
271    /**
272     * Constructor that assigns the specified Map as the backing store. The map
273     * must be empty and non-null.
274     *
275     * @param map the map to assign
276     */
277    protected AbstractMapMultiSet(final Map<E, MutableInteger> map) {
278        this.map = map;
279    }
280
281    @Override
282    public int add(final E object, final int occurrences) {
283        if (occurrences < 0) {
284            throw new IllegalArgumentException("Occurrences must not be negative.");
285        }
286
287        final MutableInteger mut = map.get(object);
288        final int oldCount = mut != null ? mut.value : 0;
289
290        if (occurrences > 0) {
291            modCount++;
292            size += occurrences;
293            if (mut == null) {
294                map.put(object, new MutableInteger(occurrences));
295            } else {
296                mut.value += occurrences;
297            }
298        }
299        return oldCount;
300    }
301
302    /**
303     * Clears the multiset by clearing the underlying map.
304     */
305    @Override
306    public void clear() {
307        modCount++;
308        map.clear();
309        size = 0;
310    }
311
312    /**
313     * Determines if the multiset contains the given element by checking if the
314     * underlying map contains the element as a key.
315     *
316     * @param object the object to search for
317     * @return true if the multiset contains the given element
318     */
319    @Override
320    public boolean contains(final Object object) {
321        return map.containsKey(object);
322    }
323
324    @Override
325    protected Iterator<Entry<E>> createEntrySetIterator() {
326        return new EntrySetIterator<>(map.entrySet().iterator(), this);
327    }
328
329    @Override
330    protected Iterator<E> createUniqueSetIterator() {
331        return new UniqueSetIterator<>(getMap().keySet().iterator(), this);
332    }
333
334    /**
335     * Read the multiset in using a custom routine.
336     * @param in the input stream
337     * @throws IOException any of the usual I/O related exceptions
338     * @throws ClassNotFoundException if the stream contains an object which class can not be loaded
339     * @throws ClassCastException if the stream does not contain the correct objects
340     */
341    @Override
342    protected void doReadObject(final ObjectInputStream in)
343            throws IOException, ClassNotFoundException {
344        final int entrySize = in.readInt();
345        for (int i = 0; i < entrySize; i++) {
346            @SuppressWarnings("unchecked") // This will fail at runtime if the stream is incorrect
347            final E obj = (E) in.readObject();
348            final int count = in.readInt();
349            map.put(obj, new MutableInteger(count));
350            size += count;
351        }
352    }
353
354    /**
355     * Write the multiset out using a custom routine.
356     * @param out the output stream
357     * @throws IOException any of the usual I/O related exceptions
358     */
359    @Override
360    protected void doWriteObject(final ObjectOutputStream out) throws IOException {
361        out.writeInt(map.size());
362        for (final Map.Entry<E, MutableInteger> entry : map.entrySet()) {
363            out.writeObject(entry.getKey());
364            out.writeInt(entry.getValue().value);
365        }
366    }
367
368    @Override
369    public boolean equals(final Object object) {
370        if (object == this) {
371            return true;
372        }
373        if (!(object instanceof MultiSet)) {
374            return false;
375        }
376        final MultiSet<?> other = (MultiSet<?>) object;
377        if (other.size() != size()) {
378            return false;
379        }
380        for (final E element : map.keySet()) {
381            if (other.getCount(element) != getCount(element)) {
382                return false;
383            }
384        }
385        return true;
386    }
387
388    /**
389     * Returns the number of occurrence of the given element in this multiset by
390     * looking up its count in the underlying map.
391     *
392     * @param object the object to search for
393     * @return the number of occurrences of the object, zero if not found
394     */
395    @Override
396    public int getCount(final Object object) {
397        final MutableInteger count = map.get(object);
398        if (count != null) {
399            return count.value;
400        }
401        return 0;
402    }
403
404    /**
405     * Utility method for implementations to access the map that backs this multiset.
406     * Not intended for interactive use outside of subclasses.
407     *
408     * @return the map being used by the MultiSet
409     */
410    protected Map<E, MutableInteger> getMap() {
411        return map;
412    }
413
414    @Override
415    public int hashCode() {
416        int total = 0;
417        for (final Map.Entry<E, MutableInteger> entry : map.entrySet()) {
418            final E element = entry.getKey();
419            final MutableInteger count = entry.getValue();
420            total += (element == null ? 0 : element.hashCode()) ^ count.value;
421        }
422        return total;
423    }
424
425    /**
426     * Returns true if the underlying map is empty.
427     *
428     * @return true if multiset is empty
429     */
430    @Override
431    public boolean isEmpty() {
432        return map.isEmpty();
433    }
434
435    /**
436     * Gets an iterator over the multiset elements. Elements present in the
437     * MultiSet more than once will be returned repeatedly.
438     *
439     * @return the iterator
440     */
441    @Override
442    public Iterator<E> iterator() {
443        return new MapBasedMultiSetIterator<>(this);
444    }
445
446    @Override
447    public int remove(final Object object, final int occurrences) {
448        if (occurrences < 0) {
449            throw new IllegalArgumentException("Occurrences must not be negative.");
450        }
451
452        final MutableInteger mut = map.get(object);
453        if (mut == null) {
454            return 0;
455        }
456        final int oldCount = mut.value;
457        if (occurrences > 0) {
458            modCount++;
459            if (occurrences < mut.value) {
460                mut.value -= occurrences;
461                size -= occurrences;
462            } else {
463                map.remove(object);
464                size -= mut.value;
465                mut.value = 0;
466            }
467        }
468        return oldCount;
469    }
470
471    /**
472     * Sets the map being wrapped.
473     * <p>
474     * <b>NOTE:</b> this method should only be used during deserialization
475     *
476     * @param map the map to wrap
477     */
478    protected void setMap(final Map<E, MutableInteger> map) {
479        this.map = map;
480    }
481
482    /**
483     * Returns the number of elements in this multiset.
484     *
485     * @return current size of the multiset
486     */
487    @Override
488    public int size() {
489        return size;
490    }
491
492    /**
493     * Returns an array of all of this multiset's elements.
494     *
495     * @return an array of all of this multiset's elements
496     */
497    @Override
498    public Object[] toArray() {
499        final Object[] result = new Object[size()];
500        int i = 0;
501        for (final Map.Entry<E, MutableInteger> entry : map.entrySet()) {
502            final E current = entry.getKey();
503            final MutableInteger count = entry.getValue();
504            for (int index = count.value; index > 0; index--) {
505                result[i++] = current;
506            }
507        }
508        return result;
509    }
510
511    /**
512     * Returns an array of all of this multiset's elements.
513     * If the input array has more elements than are in the multiset,
514     * trailing elements will be set to null.
515     *
516     * @param <T> the type of the array elements
517     * @param array the array to populate
518     * @return an array of all of this multiset's elements
519     * @throws ArrayStoreException if the runtime type of the specified array is not
520     *   a supertype of the runtime type of the elements in this list
521     * @throws NullPointerException if the specified array is null
522     */
523    @Override
524    public <T> T[] toArray(T[] array) {
525        final int size = size();
526        if (array.length < size) {
527            @SuppressWarnings("unchecked") // safe as both are of type T
528            final T[] unchecked = (T[]) Array.newInstance(array.getClass().getComponentType(), size);
529            array = unchecked;
530        }
531
532        int i = 0;
533        for (final Map.Entry<E, MutableInteger> entry : map.entrySet()) {
534            final E current = entry.getKey();
535            final MutableInteger count = entry.getValue();
536            for (int index = count.value; index > 0; index--) {
537                // unsafe, will throw ArrayStoreException if types are not compatible, see Javadoc
538                @SuppressWarnings("unchecked")
539                final T unchecked = (T) current;
540                array[i++] = unchecked;
541            }
542        }
543        while (i < array.length) {
544            array[i++] = null;
545        }
546        return array;
547    }
548
549    @Override
550    protected int uniqueElements() {
551        return map.size();
552    }
553}