io.janusproject.util.MultisetView.java Source code

Java tutorial

Introduction

Here is the source code for io.janusproject.util.MultisetView.java

Source

/*
 * $Id$
 *
 * Janus platform is an open-source multiagent platform.
 * More details on http://www.janusproject.io
 *
 * Copyright (C) 2014 Sebastian RODRIGUEZ, Nicolas GAUD, Stphane GALLAND.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.janusproject.util;

import com.google.common.base.Objects;
import com.google.common.collect.Iterators;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multiset;

import java.io.Serializable;
import java.util.AbstractCollection;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Set;

import io.janusproject.util.DataViewDelegate.Delegator;

import static com.google.common.base.Preconditions.checkNotNull;

/**
 * A view if the multiset of the keys in a {@link AbstractDMultiMapView}.
 *
 * @param <K> - the keys.
 * @param <V> - the values.
 * @author $Author: sgalland$
 * @version $FullVersion$
 * @mavengroupid $GroupId$
 * @mavenartifactid $ArtifactId$
 */
public class MultisetView<K, V> extends AbstractCollection<K>
        implements Multiset<K>, Serializable, Delegator<Multimap<K, V>> {

    private static final long serialVersionUID = -4540240956327121021L;

    private final Multimap<K, V> multimap;
    private transient ElementSet elementSet;
    private transient EntrySet entrySet;

    /**
     * @param map - the backed map.
     */
    public MultisetView(Multimap<K, V> map) {
        this.multimap = map;
    }

    @Override
    public Multimap<K, V> getDelegatedObject() {
        return this.multimap;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        }
        if (obj instanceof Multiset) {
            Multiset<?> that = (Multiset<?>) obj;

            // We can't simply check whether the entry sets are equal, since that
            // approach fails when a TreeMultiset has a comparator that returns 0
            // when passed unequal elements.
            if (size() != that.size() || entrySet().size() != that.entrySet().size()) {
                return false;
            }
            for (Entry<?> entry : that.entrySet()) {
                if (count(entry.getElement()) != entry.getCount()) {
                    return false;
                }
            }
            return true;
        }
        return false;
    }

    @Override
    public int hashCode() {
        return entrySet().hashCode();
    }

    @Override
    public String toString() {
        return entrySet().toString();
    }

    @Override
    public int size() {
        return getDelegatedObject().size();
    }

    @Override
    public boolean isEmpty() {
        return getDelegatedObject().isEmpty();
    }

    @Override
    public void clear() {
        getDelegatedObject().clear();
    }

    @SuppressWarnings("unchecked")
    @Override
    public int count(Object element) {
        try {
            Collection<V> values = getDelegatedObject().get((K) element);
            return values.size();
        } catch (ClassCastException _) {
            return 0;
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    public int remove(Object element, int occurrences) {
        if (occurrences < 0) {
            throw new IllegalArgumentException();
        }
        if (occurrences > 0) {
            try {
                K key = (K) element;
                Collection<V> values = getDelegatedObject().get(key);
                if (values.isEmpty()) {
                    return 0;
                }

                int oldCount = values.size();

                int numberRemoved;
                if (oldCount > occurrences) {
                    numberRemoved = occurrences;
                } else {
                    numberRemoved = oldCount;
                }

                Iterator<V> valueIterator = values.iterator();
                for (int i = 0; i < numberRemoved && valueIterator.hasNext(); ++i) {
                    valueIterator.next();
                    valueIterator.remove();
                }

                return oldCount;
            } catch (ClassCastException _) {
                //
            }
        }
        return count(element);
    }

    @Override
    public int setCount(K element, int count) {
        if (count < 0) {
            throw new IllegalArgumentException();
        }
        int currentCount = count(element);
        return setCountImpl(element, currentCount, count);
    }

    @Override
    public boolean setCount(K element, int oldCount, int newCount) {
        if (oldCount < 0 || newCount < 0) {
            throw new IllegalArgumentException();
        }
        int count = count(element);
        if (count == oldCount) {
            setCountImpl(element, count, newCount);
            return true;
        }
        return false;
    }

    private int setCountImpl(K element, int oldCount, int newCount) {
        int delta = newCount - oldCount;
        if (delta > 0) {
            add(element, delta);
        } else if (delta < 0) {
            remove(element, -delta);
        }
        return oldCount;
    }

    @Override
    public boolean addAll(Collection<? extends K> c) {
        if (c.isEmpty()) {
            return false;
        }
        if (c instanceof Multiset) {
            Multiset<? extends K> that = (Multiset<? extends K>) c;
            for (Entry<? extends K> entry : that.entrySet()) {
                add(entry.getElement(), entry.getCount());
            }
        } else {
            Iterators.addAll(this, c.iterator());
        }
        return true;
    }

    @Override
    public Set<K> elementSet() {
        if (this.elementSet == null) {
            this.elementSet = new ElementSet();
        }
        return this.elementSet;
    }

    @Override
    public Set<Entry<K>> entrySet() {
        if (this.entrySet == null) {
            this.entrySet = new EntrySet();
        }
        return this.entrySet;
    }

    @Override
    public Iterator<K> iterator() {
        return new MultisetIterator(entrySet().iterator());
    }

    @Override
    public boolean contains(Object element) {
        return count(element) > 0;
    }

    @Override
    public boolean remove(Object element) {
        return remove(element, 1) > 0;
    }

    @Override
    public int add(K element, int occurrences) {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean add(K element) {
        throw new UnsupportedOperationException();
    }

    /**
     * @author $Author: sgalland$
     * @version $FullVersion$
     * @mavengroupid $GroupId$
     * @mavenartifactid $ArtifactId$
     */
    private class EntrySet extends AbstractSet<Entry<K>> {

        /**
         */
        public EntrySet() {
            //
        }

        /**
         * Replies the associated multiset.
         *
         * @return the associated multiset.
         */
        protected Multiset<K> multiset() {
            return MultisetView.this;
        }

        @Override
        public boolean contains(Object o) {
            if (o instanceof Entry) {
                Entry<?> entry = (Entry<?>) o;
                if (entry.getCount() <= 0) {
                    return false;
                }
                int count = multiset().count(entry.getElement());
                return count == entry.getCount();

            }
            return false;
        }

        @SuppressWarnings({ "unchecked", "rawtypes" })
        @Override
        public boolean remove(Object object) {
            if (object instanceof Multiset.Entry) {
                Entry<?> entry = (Entry<?>) object;
                Object element = entry.getElement();
                int entryCount = entry.getCount();
                if (entryCount != 0) {
                    // Safe as long as we never add a new entry, which we won't.
                    Multiset<Object> multiset = (Multiset) multiset();
                    return multiset.setCount(element, entryCount, 0);
                }
            }
            return false;
        }

        @Override
        public void clear() {
            multiset().clear();
        }

        @Override
        public Iterator<Entry<K>> iterator() {
            final Iterator<K> backingEntries = getDelegatedObject().keySet().iterator();
            return new Iterator<Entry<K>>() {
                private boolean canRemove;

                @Override
                public boolean hasNext() {
                    return backingEntries.hasNext();
                }

                @Override
                public Multiset.Entry<K> next() {
                    final K entryKey = backingEntries.next();
                    final Collection<V> entryValues = getDelegatedObject().get(entryKey);
                    this.canRemove = true;
                    return new Entry<K>() {
                        @Override
                        public boolean equals(Object object) {
                            if (object instanceof Multiset.Entry) {
                                Multiset.Entry<?> that = (Multiset.Entry<?>) object;
                                return this.getCount() == that.getCount()
                                        && Objects.equal(getElement(), that.getElement());
                            }
                            return false;
                        }

                        @Override
                        public int hashCode() {
                            K e = getElement();
                            return ((e == null) ? 0 : e.hashCode()) ^ getCount();
                        }

                        @Override
                        public String toString() {
                            StringBuilder b = new StringBuilder();
                            b.append(String.valueOf(getElement()));
                            int n = getCount();
                            if (n != 1) {
                                b.append(" x "); //$NON-NLS-1$
                                b.append(n);
                            }
                            return b.toString();
                        }

                        @Override
                        public K getElement() {
                            return entryKey;
                        }

                        @Override
                        public int getCount() {
                            Collection<V> values = entryValues;
                            if (values == null || values.size() == 0) {
                                values = getDelegatedObject().get(getElement());
                            }
                            return (values == null) ? 0 : values.size();
                        }
                    };
                }

                @Override
                public void remove() {
                    if (!this.canRemove) {
                        throw new IllegalStateException();
                    }
                    backingEntries.remove();
                    this.canRemove = false;
                }
            };
        }

        @Override
        public int size() {
            return getDelegatedObject().keySet().size();
        }
    }

    /**
     * @author $Author: sgalland$
     * @version $FullVersion$
     * @mavengroupid $GroupId$
     * @mavenartifactid $ArtifactId$
     */
    private class ElementSet extends AbstractSet<K> {

        /**
         */
        public ElementSet() {
            //
        }

        /**
         * Replies the associated multiset.
         *
         * @return the associated multiset.
         */
        protected Multiset<K> multiset() {
            return MultisetView.this;
        }

        @Override
        public void clear() {
            multiset().clear();
        }

        @Override
        public boolean contains(Object o) {
            return multiset().contains(o);
        }

        @Override
        public boolean containsAll(Collection<?> c) {
            return multiset().containsAll(c);
        }

        @Override
        public boolean isEmpty() {
            return multiset().isEmpty();
        }

        @Override
        public Iterator<K> iterator() {
            return new TransformedIterator(multiset().entrySet().iterator()) {
                @Override
                protected K transform(Entry<K> entry) {
                    return entry.getElement();
                }
            };
        }

        @Override
        public boolean remove(Object o) {
            int count = multiset().count(o);
            if (count > 0) {
                multiset().remove(o, count);
                return true;
            }
            return false;
        }

        @Override
        public int size() {
            return multiset().entrySet().size();
        }

    }

    /**
     * @author $Author: sgalland$
     * @version $FullVersion$
     * @mavengroupid $GroupId$
     * @mavenartifactid $ArtifactId$
     */
    private class MultisetIterator implements Iterator<K> {

        private final Iterator<Entry<K>> entryIterator;
        private Entry<K> currentEntry;
        /**
         * Count of subsequent elements equal to current element.
         */
        private int laterCount;
        /**
         * Count of all elements equal to current element.
         */
        private int totalCount;
        private boolean canRemove;

        public MultisetIterator(Iterator<Entry<K>> entryIterator) {
            this.entryIterator = entryIterator;
        }

        @Override
        public boolean hasNext() {
            return this.laterCount > 0 || this.entryIterator.hasNext();
        }

        @Override
        public K next() {
            if (!hasNext()) {
                throw new NoSuchElementException();
            }
            if (this.laterCount == 0) {
                this.currentEntry = this.entryIterator.next();
                this.laterCount = this.currentEntry.getCount();
                this.totalCount = this.laterCount;
            }
            this.laterCount--;
            this.canRemove = true;
            return this.currentEntry.getElement();
        }

        @Override
        public void remove() {
            if (!this.canRemove) {
                throw new IllegalStateException();
            }
            if (this.totalCount == 1) {
                this.entryIterator.remove();
            } else {
                MultisetView.this.remove(this.currentEntry.getElement());
            }
            this.totalCount--;
            this.canRemove = false;
        }
    }

    /**
     * @author $Author: sgalland$
     * @version $FullVersion$
     * @mavengroupid $GroupId$
     * @mavenartifactid $ArtifactId$
     */
    private abstract class TransformedIterator implements Iterator<K> {
        final Iterator<? extends Entry<K>> backingIterator;

        /**
         * @param backingIterator - the iterator.
         */
        public TransformedIterator(Iterator<? extends Entry<K>> backingIterator) {
            this.backingIterator = checkNotNull(backingIterator);
        }

        protected abstract K transform(Entry<K> from);

        @Override
        public final boolean hasNext() {
            return this.backingIterator.hasNext();
        }

        @Override
        public final K next() {
            return transform(this.backingIterator.next());
        }

        @Override
        public final void remove() {
            this.backingIterator.remove();
        }
    }

}