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.bag;
018
019import java.io.IOException;
020import java.io.ObjectInputStream;
021import java.io.ObjectOutputStream;
022import java.util.Collection;
023import java.util.Iterator;
024
025import org.apache.commons.collections4.SortedBag;
026
027/**
028 * Decorates another {@link SortedBag} to comply with the Collection contract.
029 *
030 * @param <E> the type of elements in this bag
031 * @since 4.0
032 */
033public final class CollectionSortedBag<E> extends AbstractSortedBagDecorator<E> {
034
035    /** Serialization version */
036    private static final long serialVersionUID = -2560033712679053143L;
037
038    /**
039     * Factory method to create a sorted bag that complies to the Collection contract.
040     *
041     * @param <E> the type of the elements in the bag
042     * @param bag  the sorted bag to decorate, must not be null
043     * @return a SortedBag that complies to the Collection contract
044     * @throws NullPointerException if bag is null
045     */
046    public static <E> SortedBag<E> collectionSortedBag(final SortedBag<E> bag) {
047        return new CollectionSortedBag<>(bag);
048    }
049
050    /**
051     * Constructor that wraps (not copies).
052     *
053     * @param bag  the sorted bag to decorate, must not be null
054     * @throws NullPointerException if bag is null
055     */
056    public CollectionSortedBag(final SortedBag<E> bag) {
057        super(bag);
058    }
059
060    @Override
061    public boolean add(final E object) {
062        return add(object, 1);
063    }
064
065    @Override
066    public boolean add(final E object, final int count) {
067        decorated().add(object, count);
068        return true;
069    }
070
071    // Collection interface
072
073    @Override
074    public boolean addAll(final Collection<? extends E> coll) {
075        boolean changed = false;
076        for (final E current : coll) {
077            final boolean added = add(current, 1);
078            changed = changed || added;
079        }
080        return changed;
081    }
082
083    @Override
084    public boolean containsAll(final Collection<?> coll) {
085        return coll.stream().allMatch(this::contains);
086    }
087
088    /**
089     * Deserializes the collection in using a custom routine.
090     *
091     * @param in  the input stream
092     * @throws IOException if an error occurs while reading from the stream
093     * @throws ClassNotFoundException if an object read from the stream cannot be loaded
094     * @throws ClassCastException if deserialized object has wrong type
095     */
096    @SuppressWarnings("unchecked") // will throw CCE, see Javadoc
097    private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
098        in.defaultReadObject();
099        setCollection((Collection<E>) in.readObject());
100    }
101
102    @Override
103    public boolean remove(final Object object) {
104        return remove(object, 1);
105    }
106
107    @Override
108    public boolean removeAll(final Collection<?> coll) {
109        if (coll != null) {
110            boolean result = false;
111            for (final Object obj : coll) {
112                final boolean changed = remove(obj, getCount(obj));
113                result = result || changed;
114            }
115            return result;
116        }
117        // let the decorated bag handle the case of null argument
118        return decorated().removeAll(null);
119    }
120
121    @Override
122    public boolean retainAll(final Collection<?> coll) {
123        if (coll != null) {
124            boolean modified = false;
125            final Iterator<E> e = iterator();
126            while (e.hasNext()) {
127                if (!coll.contains(e.next())) {
128                    e.remove();
129                    modified = true;
130                }
131            }
132            return modified;
133        }
134        // let the decorated bag handle the case of null argument
135        return decorated().retainAll(null);
136    }
137
138    /**
139     * Serializes this object to an ObjectOutputStream.
140     *
141     * @param out the target ObjectOutputStream.
142     * @throws IOException thrown when an I/O errors occur writing to the target stream.
143     */
144    private void writeObject(final ObjectOutputStream out) throws IOException {
145        out.defaultWriteObject();
146        out.writeObject(decorated());
147    }
148
149}